Categories
Lodash

Lodash Methods Implemented with Plain JavaScript — Flattening Arrays and Searching

Lodash is a very useful utility library that lets us work with objects and arrays easily.

However, now that the JavaScript standard library is catching up to libraries such as Lodash, we can implement many of the functions in simple ways.

In this article, we’ll look at how to flatten arrays recursive with flattenDeep and flattenDepth , and converting array key-value pairs to objects with fromPair .

flattenDeep

The Lodash’s flattenDeep recursively flattens an array. Now that JavaScript has a flat instance method for flattening arrays, we don’t need this method anymore.

The only thing that’s better is that it returns a new array instead of modifying the original one.

For instance, we can use it as follows to make our own flattenDeep method;

const flattenDeep = arr => arr.flat(Infinity)

Passing in Infinity flattens recursively.

Then we can call it as follows:

const flattened = flattenDeep([1, [2],
  [
    [3]
  ], 4, 5
])

Then we get that flattened is [1, 2, 3, 4, 5] . The harder way is to implement it from scratch ourselves. However, we can make it shorter with the spread operator as follows:

const flattenDeep = arr => {
  let flattened = [];
  for (const a of arr) {
    if (Array.isArray(a)) {
      flattened = [...flattened, ...a];
      flattened = [...flattenDeep(flattened)]
    } else {
      flattened.push(a);
    }
  }
  return flattened;
}

In the code above, we called our ownflattenDeep function recursively. We only call it recursively when the entry is an array.

Therefore, they both get us the same result.

flattenDepth

The flattenDepth method recursively flatten an array up to the given depth level.

As we can see from the flattenDeep example above, the JavaScript’s built-in flat method takes an argument to specify the depth to flatten.

Therefore, we can implement our own flattenDepth function as follows:

const flattenDepth = (arr, depth) => arr.flat(depth)

We just call flat with our own depth . Therefore, when we call it as follows:

const flattened = flattenDepth([1, [2],
  [
    [3]
  ], 4, 5
], 1)

We get that flattened is:

[
  1,
  2,
  [
    3
  ],
  4,
  5
]

since specified that we flatten the given array one level deep.

If we want to implement flattenDepth ourself, we can implement something similar to how we implemented flattenDeep from scratch:

const flattenDepth = (arr, depth, flattenedDepth = 0) => {
  let flattened = [];
  for (const a of arr) {
    if (Array.isArray(a)) {
      flattened = [...flattened, ...a];
      if (depth < flattenedDepth) {
        flattened = [...flattenDepth(flattened, flattenedDepth + 1)]
      }
    } else {
      flattened.push(a);
    }
  }
  return flattened;
}

In the code above, we have an extra flattenDepth parameter, which is set to 0 so that we can keep track of the depth of the array that’s been flattened.

We then only call it when the

Then we can increase the flattenedDepth by one when we recursively call flattenedDepth .

Therefore, we get the same result as we did with flattenDepth implemented with the array’s flat method.

fromPairs

The fromPairs method converts return an object with that has an array’s entry with the key as the first entry and the value as the second entry.

JavaScript already has an Object.fromEntries that does the same thing, we can just use it as follows:

const obj = Object.fromEntries([
  ['a', 1],
  ['b', 2]
])

Then we can get that the obj object is:

{
  "a": 1,
  "b": 2
}

Object.fromEntries returns the same result as fromPairs , so we definitely don’t need fromPairs anymore.

indexOf

The Lodash indexOf method is the same as the plain JavaScript’s indexOf method. The Lodash indexOf can take a start index to search for an item with, which the plain indexOf method also takes.

For instance, we can use the plainindexOf method to implement the indexOf method as follows:

const indexOf = (arr, value, start) => arr.indexOf(value, start)

As we can see, we just used all the arguments with the indexOf method. The only difference is that indexOf is called on arr , which is an array instance.

Conclusion

We can call our own array flattening function or the array instance’s flat method, which both flatten an array.

The Object.fromEntries method replaces the fromPair method in Lodash.

Finally, the array instance’sindexOf method replaces the Lodash’s indexOf method.

Categories
Lodash

Learning JavaScript by Implementing Lodash Methods — Object Traversal

Lodash is a very useful utility library that lets us work with objects and arrays easily.

However, now that the JavaScript standard library is catching up to libraries such as Lodash, we can implement many of the functions in simple ways.

In this article, we’ll look at how to implement some methods for traversing through object keys.

forOwn

The Lodash forOwn method iterates through an object’s own enumerable string keyed properties and run the iteratee function on each property.

The iteratee takes 3 arguments, which are value , key and object . where value is the value of the property that’s being looped through. key is the property key string, object is the object that’s being traversed.

We can get all the object’s keys with the Object.keys method as an array, so we can loop through the keys as follows:

const forOwn = (obj, iteratee) => {
  for (const key of Object.keys(obj)) {
    const result = iteratee(obj[key], key, obj);
    if (result === false) {
      break;
    }
  }
}

In the code above, we called Object.keys with obj to get the own keys of obj . Then we call iteratee with the value by referencing obj[key] as the 1st argument, key as the 2nd argument, and obj as the 3rd argument.

iteratee is called on each iteration and its return value is set as the value of result .

Then when we call it as follows:

function Foo() {
  this.a = 1;
  this.b = 2;
}

Foo.prototype.c = 3;

forOwn(new Foo(), (value, key) => console.log(key));

We get a and b from the console log output since a and b are the Foo ‘s instance’s own property, but c is an inherited property from Foo ‘s prototype.

forOwnRight

Lodash’s forOwnRight method is like forOwn except that it loops through the object’s keys in reverse order.

We can just change forOwn ‘s implementation slightly by calling reverse on the array returned by Object.keys to loop through the keys in reverse.

For instance, we can implement that as follows:

const forOwnRight = (obj, iteratee) => {
  for (const key of Object.keys(obj).reverse()) {
    const result = iteratee(obj[key], key, obj);
    if (result === false) {
      break;
    }
  }
}

In the code above, we have the same implementation as forOwn except that we called reverse after Object.keys .

Then when we call it as follows:

function Foo() {
  this.a = 1;
  this.b = 2;
}

Foo.prototype.c = 3;

forOwnRight(new Foo(), (value, key) => console.log(key));

We see b then a logged in the console log output.

functions

The functions method returns the array with the property names of own properties that are function valued.

We can implement that ourselves by checking whether the given property has type function and put it in the returned array.

For instance, we can do that as follows:

const functions = (obj) => Object.entries(obj).filter(([key, value]) => typeof value === 'function').map(([key]) => key);

In the code above, we used Object.entries to get the key and value of each property of obj as a nested array with each entry as an array of the key and value.

Then we called filter on what’s returned by Object.entries by destructuring the given key and value on each entry, and then return whatever value has the 'function' type by checking value with the typeof operator.

Next, we called map on with the key destructured from the array to return the keys only and put them in the returned array.

Then when we have a Foo constructor and call it with the Foo instance as follows:

function Foo() {
  this.a = () => 'a';
  this.b = () => 'b';
}

Foo.prototype.c = () => 'c';

const result = functions(new Foo());

We get that result is [“a”, “b”] since in the Foo instance, only 'a' and 'b' are function valued own properties.

Conclusion

To implement the forOwn and forOwnRight Lodash methods, we can use the Object.keys method to get all the keys and then use the for...of loop to loop through the own keys of the given object.

The functions method can be implemented by calling Object.entries to get the keys and values of an object as an array.

Then we can call the filter method with the typeof operator to check the ones that value type 'function' . Then we can get the keys of the function valued properties and return an array with them with map.

Categories
Lodash

Learning JavaScript by Implementing Lodash Methods — Converting Objects

Lodash is a very useful utility library that lets us work with objects and arrays easily.

However, now that the JavaScript standard library is catching up to libraries such as Lodash, we can implement many of the functions in simple ways.

In this article, we’ll look at how to implement Lodash methods to convert objects.

toArray

The Lodash toArray method transforms a given value to an array.

For instance, we can implement as follows:

const toArray = val => {
  if (val instanceof String || typeof val === 'string') {
    return val.split('');
  } else if (val instanceof Object) {
    return Object.values(val)
  }
  return [];
}

In the code above, we checked if val is a string by using the instanceof operator for strings created with the String constructor and use the typeof operator to check for primitive strings.

Then we split the strings into with the split method in this case.

If val is an object, then we return the values with Object.values .

Otherwise, we return an empty array.

Then we can call it as follows:

console.log(toArray({
  'a': 1,
  'b': 2
}));

console.log(toArray('abc'));

console.log(toArray(1));

console.log(toArray(null));

The first call should return [1, 2] , the 2nd returns ['a', 'b', 'c'] , and the last 2 return an empty array.

toFinite

Lodash’s toFinite method converts values to finite values.

We can do that by checking for Infinity and -Infinity and then returning the finite values accordingly.

For instance, we can write the following function to do that:

const toFinite = (val) => {
  if (val === Infinity) {
    return Number.MAX_VALUE
  } else if (val === -Infinity) {
    return -Number.MAX_VALUE
  }
  return isNaN(val) ? 0 : +val;
}

In the code above, we checked if val is Infinity or -Infinity and return Number.MAX_VALUE and — Number.MAX_VALUE respectively.

Otherwise, we use the isNaN function to check is val can be converted to a number. The isNaN function tries to convert the argument into a number before checking if the converted value is NaN or not.

If it’s not NaN , then we return val converted to a number. Otherwise, we return 0.

Then we can call it as follows:

console.log(toFinite(3.2));
console.log(toFinite(Number.MIN_VALUE));
console.log(toFinite(Infinity));
console.log(toFinite('3.2'));

Then we get:

3.2
5e-324
1.7976931348623157e+308
3.2

from the console log output.

toInteger

Lodash’s toInteger method converted the given value to an integer.

We can implement our own toInteger function as follows:

const toInteger = (val) => {
  if (val === Infinity) {
    return Number.MAX_VALUE
  } else if (val === -Infinity) {
    return -Number.MAX_VALUE
  }
  return isNaN(val) ? 0 : Math.round(+val);
}

In the code above, we check if val is Infinity our -Infinity , then we return Number.MAX_VALUE and -Number.MAX_VALUE respectively.

Otherwise, we check if val can be converted to a number, and then call Math.round if the number can be converted to a number.

Then we can call it as follows:

console.log(toInteger(3.2));
console.log(toInteger(Number.MIN_VALUE));
console.log(toInteger(Infinity));
console.log(toInteger('3.2'));

We then get the following values logged for each function call:

3
0
1.7976931348623157e+308
3

toLength

The toLength method converts a number to an integer that’s suitable to be used as the length of an array-like object.

We can implement our own toLength method as follows:

const toLength = (val) => {
  if (val === Infinity || Math.round(+val) > 4294967295) {
    return 4294967295
  } else if (+val < 0) {
    return 0
  }
  return isNaN(val) ? 0 : Math.round(+val);
}

In the code above, we check that if val is Infinity or if the converted integer is bigger than 4294967295, which is the maximum length of an array.

If it is, then we return 4294967295. If val converted to a number if negative, then we return 0.

Otherwise, we call Math.round to round the converted number if the converted value is a number.

Then when we call it as follows:

console.log(toLength(3.2));
console.log(toLength(Number.MIN_VALUE));
console.log(toLength(Infinity));
console.log(toLength('3.2'));

We get the following values from the console log outputs:

3
0
4294967295
3

Conclusion

We can implement Lodash’s object conversion methods easily ourselves.

The toArray method can be implemented by splitting strings or getting the values of an object.

The toFinite method can be implemented by returning the biggest integer in JavaSscript if it’s Infinity and the negative of that if it’s -Infinity . Otherwise, if it’s a number, then we return the 0 if the value isn’t a number or the number itself if it can be converted into a number.

Likewise, the toLength method can implemented in a similar way except that instead of returning the largest integer for Infinity and negative of that for -Infinity , we return the maximum length of a JavaScript array for Infinity and 0 for -Infinity or any other negative number.

Categories
Lodash

Learning JavaScript by Implementing Lodash Methods — Comparison and Constructors

Lodash is a very useful utility library that lets us work with objects and arrays easily.

However, now that the JavaScript standard library is catching up to libraries such as Lodash, we can implement many of the functions in simple ways.

In this article, we look at how to clone objects in a variety of ways and do comparisons on object values.

conformsTo

The Lodash conformsTo checks if all object entries conform to a given predicate.

We can check the value of each property to see if the condition is met as follows:

const conformsTo = (obj, predicate) => {
  for (const [key, val] of Object.entries(obj)) {
    if (typeof predicate[key] === 'function' && predicate[key](val)) {
      return true
    }
  }
  return false;
}

In the code above, we used the Object.entries method to get all the entries of an object. Then we used destructuring to destructure the array entries returned by Object.entries to the keys and values.

Then we check val against the predicate[key] function, which returns the true if the condition in it is met and false otherwise.

Then we can use it as follows:

const result = conformsTo({
  a: 1
}, {
  a: x => x >= 0
});

Then since a has value 1, it conformsTo should return true because each value if bigger than or equal to 0 as the predicate has condition x >= 0 as the value of the a property.

eq

The Lodash eq object compares 2 objects to see if they’re equivalent using the SameValueZero algorithm.

We can implement our own eq function by doing the comparing for +0 and -0 ourselves and using Object.is for everything else:

const eq = (a, b) => {
  if (a === +0 && b === -0 ||
    a === -0 && b === +0 ||
    a === 0 && b === -0 ||
    a === 0 && b === +0 ||
    a === +0 && b === 0 ||
    a === -0 && b === 0) {
    return true;
  }
  return Object.is(a, b);
}

In the code above, we compared the 0 values with different signs and return true for all of them.

Then we use object Object.is for all other comparisons. When we call it as follows:

const result = eq(1, -0);

We get that result is false and if we call it as follows:

const result = eq(+0, -0);

we get that result is true .

gt

The Lodash gt method returns a boolean indicating if one number is bigger than the other.

We can just use the > operator to do the comparison ourselves. We can implement it as follows:

const gt = (a, b) => a > b;

Then we can call it as follows:

const result = gt(2, 1);

result is true .

gte

Like gt , we can implement the Lodash gte method with the >= operator.

We can write the following function as follows:

const gt = (a, b) => a >= b;

Then when we write the following code:

const result = gt(2, 2);

We get that result is true .

isArray

The Lodash isArray method checks is a value is an array. We can just use the Array.isArray method to check if an object is an array.

Therefore, we can write the following code to implement our own isArray method:

const isArray = arr => Array.isArray(arr);

In the code above, we just called Array.isArray to check if arr is an array.

Then we can call it as follows:

const result = isArray([1, 2, 3]);

We get that result is true .

isDate

The Lodash isDate method checks if an object is an instance of the Date constructor.

We can use the constructor.name property of an object to check what constructor the object is created from.

Therefore, we can write the following to implement our own isDate function:

const isDate = date => date.constructor.name == 'Date'

Then we can call the function as follows:

const result = isDate(1);

and get that result is false . If we call it as follows:

const result = isDate(new Date());

We get that result is true .

Conclusion

We can use the constuctor.name property to get the name of the constructor that an object is created from.

To check if an object is an array, we can use the Array.isArray method to implement it.

To compare values for equality, we can use === and Object.is . Finally, we can use the > and >= to check if something is bigger than another.

Categories
Lodash

Learning JavaScript by Implementing Lodash Methods — Picking Items and Getting Sizes

Lodash is a very useful utility library that lets us work with objects and arrays easily.

However, now that the JavaScript standard library is catching up to libraries such as Lodash, we can implement many of the functions in simple ways.

In this article, we’ll look at how to implement Lodash methods that let us pick items and getting sizes of collections.

reject

The reject method takes a collection of items and a predicate with the items that we don’t want to include in the returned array and return an array with the items that are opposite of the predicate.

We can implement this as follows:

const reject = (collection, predicate) => collection.filter(c => !predicate(c));

In the code above, we just called filter with a callback that returns the predicate call negated.

Then we can call it as follows:

const users = [{
    'user': 'foo',
    'active': false
  },
  {
    'user': 'bar',
    'active': true
  }
];

const result = reject(users, o => !o.active);

The code above rejects the users entries that have active set to false as the value.

This means that we return the entries with active set to true . Therefore, we get:

[
  {
    "user": "bar",
    "active": true
  }
]

as the value of result .

sample

The Lodash sample method returns a random element from a collection. Since plain JavaScript has a Math.random method, we can use that to get a random element from an array as follows:

const sample = (collection) => {
  const index = Math.random() * (collection.length);
  return collection[Math.floor(index)];
};

In the code above, we just used the Math.random method which is multiplied by the collection.length to get the index . Then we take the floor of it to get the actual index.

Then when we call it as follows:

const result = sample([1, 2, 3]);

We get a random item from the array.

sampleSize

The sampleSize method takes an array to get the items from and the size of the sample, we want to get.

It returns the array that takes random elements from the collection up to the given size .

We can implement the sampleSize method as follows:

const sampleSize = (collection, size) => {
  let sampled = [];
  for (let i = 1; i <= size; i++) {
    const index = Math.random() * (collection.length);
    sampled.push(collection.splice(index, 1)[0]);
  }
  return sampled;
};

In the code above, we created a loop that gets the index of the collection to pick by calling the Math.random multiplied by collection.length .

Then we called splice on collection to remove that element from collection and push it into the sampled array.

Once we have enough sampled items, we return sampled .

Then when we call it as follows:

const result = sampleSize([1, 2, 3], 2);

We get 2 different items from the array we passed in as the value of result .

shuffle

The Lodash shuffle method returns an array that’s a shuffled version of the original array.

We can implement it as follows:

const shuffled = (collection) => {
  let shuffled = [];
  const originalLength = collection.length;
  for (let i = originalLength - 1; i >= 0; i--) {
    const index = Math.random() * (collection.length);
    shuffled.push(collection.splice(index, 1)[0]);
  }
  return shuffled;
};

In the code above, we created a loop that loop through the collection ‘s original length. Then we get a random item from the collection and put it into the shuffled array as we did with the sampleSize function above.

Once the loop is done, we returned the shuffled array.

Then when we call shuffle as follows:

const result = shuffled([1, 2, 3]);

We get a shuffled version of the array we passed in.

size

The size method gets the size of a collection, which can be an array or an object. Since we have the length property of arrays and we have the Object.keys method for objects, we can implement it with the following code:

const size = (collection) => {
  if (Array.isArray(collection)) {
    return collection.length;
  } else {
    return Object.keys(collection).length;
  }
};

In the code above, we check if collection is an array. If it is, then we return the length property of it.

Otherwise, we return the array returned by Object.keys ‘s length property.

When we call it as follows:

const result = size([1, 2, 3]);

We get that result is 3.

When we call it with an object:

const result = size({
  'a': 1,
  'b': 2
});

We get that result is 2.

Conclusion

The reject method can be implemented with the filter method by negating the predicate in the callback.

The sample family of methods and shuffle can be implemented with Math.random and splice .

Finally, the size method can be implemented with the length of the array or length of the array returned by Object.keys .