Categories
Lodash

Learning JavaScript by Implementing Lodash Methods — Type Checks

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 Lodash methods for doing type checks.

isBoolean

The Lodash isBoolean method checks whether a value is a boolean.

We can easily check if a value is a boolean with the typeof operator. For instance, we can write the following code:

const isBoolean = val => typeof val === 'boolean'

In the code above, we just used the typeof operator to check is val is a boolean.

Then we can call it as follows:

const result = isBoolean(true);

result is true since true is a boolean. Otherwise, if we run:

const result = isBoolean(1);

We get that result is false since 1 isn’t a boolean.

isArrayBuffer

The Lodash isArrayBuffer checks if an object is an instance of the ArrayBuffer constructor. We can use the constructor.name property of an object to do that check.

Therefore, we can write our own function to do the same thing as follows:

const isArrayBuffer = val => val.constructor.name === 'ArrayBuffer'

In the code above, we just used the constructor.name property to check if an object is an instance of theArrayBuffer constructor.

We can also use the instanceof operator to check if val is an instance of the ArrayBuffer as follows:

const isArrayBuffer = val => val instanceof ArrayBuffer

Then we can call it as follows:

const result = isArrayBuffer(new ArrayBuffer());

and result should be true since it’s an instance of ArrayBuffer .

isArrayLike

The isArrayLike method checks if an object is an array-like object by checking if the length property exists and has a value that’s greater than or equal to 0 or less than or equal to Number.MAX_SAFE_INTEGER .

With these criteria, we can implement our own isArrayLike function as follows:

const isArrayLike = val => typeof val.length === 'number' && val.length >= 0 && val.length <= Number.MAX_SAFE_INTEGER

In the code above, we check if val.length is a number with the typeof operator. Then we check if val.length is between 0 and Number.MAX_SAFE_INTEGER .

Then if we run our isArrayLike function as follows:

const result = isArrayLike('foo');

We get that result is true since a string has a length property that’s between 0 and Number.MAX_SAFE_INTEGER .

On the other hand, if we call isArrayLike with 1 as follows:

const result = isArrayLike(1);

Then we get that result is false since 1 doesn’t have a length property.

isElement

The Lodash isElement method checks whether an object is an HTML element. Since all HTML elements are an instance of the HTMLElement constructor, we can use the instanceof operator to check if an object is an instance of that constructor as follows:

const isElement = obj => obj instanceof HTMLElement

In the code above, we check if obj is an element by checking if it’s an instance of HTMLElement .

Then when we run it as follows:

const result = isElement(document.body);

We get that result is true since document.body is an HTML element, which means that it’s an instance of the HTMLElement constructor.

On the other hand, if we call it as follows:

const result = isElement('foo');

Then we get that result is false because it’s not an HTMLElement object.

isEmpty

Lodash’s isEmpty method checks whether an object, collection, map, and set is an empty object.

Objects are empty if they have no enumerable own properties. Array-like values like arguments , arrays, strings, etc. are considered empty is the length is 0.

Maps and sets are considered empty if their length is 0.

With the conditions clearly defined, we can write our own function to do the isEmpty check.

We can implement our own isEmpty function as follows:

const isEmpty = obj => {
  if (typeof obj.length === 'number') {
    return obj.length === 0;
  } else if (obj instanceof Map || obj instanceof Set) {
    return obj.size === 0;
  } else {
    return Object.keys(obj).length === 0;
  }
}

In the code above, we check if the length property is a number to check whether obj is an array-like object. If it is, then we return obj.length === 0 .

If obj is an instance of the Map or Set constructor, we can check the size property instead of the length property.

Otherwise, we use Object.keys with obj as the argument and get the length property and check if it’s 0.

Then if we call it as follows:

const result = isEmpty('');

We get that result is true since an empty string has length 0.

Conclusion

We can do type checking of primitive values with the typeof operator. For objects, it’s better to use the instanceof operator to get the name of the constructor as a string.

To get the size of an array-like object, we use the length property. Otherwise, we use the size property for Map and Set instances.

Categories
Lodash

Learning JavaScript by Implementing Lodash Methods — Merging 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 some Lodash object merging methods ourselves with the spread operator and plain JavaScript object operators and methods.

assign

The Lodash’s assign method assigns all of an object’s own enumerable string keyed properties of the source object to the destination object.

Source objects are applied from left to right. Plain JavaScript already has the Object.assign method in its standard library, so we can use that to implement this method.

The assign method mutates the original object to add the keys of the other objects to it.

For instance, we can do that as follows:

const assign = (obj, ...sources) => {
  for (const s of sources) {
    obj = {
      ...obj,
      ...s
    };
  }
  return obj;
}

In the code above, we have our own assign function, which takes the same arguments as Lodash’s assign method. It takes obj , which is the original object. The sources are the objects that we want to merge into obj .

Then we have a for...of loop, which loops through the sources , which is an array of object originating from the arguments that we passed in because we applied the rest operator to it.

In the loop, we applied the spread operator to obj and each object in sources to merge them into one. Then we return obj .

We can also use the plain JavaScript’s Object.assign method to do the same thing as follows:

const assign = (obj, ...sources) => {
  return Object.assign(obj, ...sources);
}

We just called Object.assign with obj as the first argument, and the sources array spread as the arguments of the assign method’s subsequent arguments.

In the end, we can call either one as follows:

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

function Bar() {
  this.c = 3;
}

Foo.prototype.b = 2;
Bar.prototype.d = 4;

console.log(assign({
  'a': 0
}, new Foo(), new Bar()));

Then we get that the console log outputs {a: 1, c: 3} since it merges in the source objects’ own enumerable properties only, which don’t include any properties in the prototype.

assignIn

The Lodash assignIn method is like the assign method, but it also merges the source object’s prototypes’ properties into the object.

Therefore, we can implement it in a similar way as the assign function, except that we need to call the Object.getPrototypeOf method to get the prototype of the objects that we have in sources so that we can merge their prototypes’ properties into our returned object.

For instance, we can implement assignIn in the following way:

const assignIn = (obj, ...sources) => {
  for (const s of sources) {
    let source = s;
    let prototype = Object.getPrototypeOf(s)
    while (prototype) {
      source = {
        ...source,
        ...prototype
      }
      prototype = Object.getPrototypeOf(prototype)
    }
    obj = {
      ...obj,
      ...source
    };
  }
  return obj;
}

In the code above, we have the same parameters and the same for...of loop as assignIn .

However, inside the loop, we have the source and prototype variables. The prototype variable is used to traverse up the object’s prototype chain to merge all its prototypes’ properties into it up the prototype chain. We did the traversal with the Object.getPrototypeOf method and the while loop.

Then we used the spread operator to merge in the source object with all its prototypes’ properties merged in into the obj object.

Now, when we call assignIn as follows:

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

function Bar() {
  this.c = 3;
}

Foo.prototype.b = 2;
Bar.prototype.d = 4;

console.log(assignIn({
  'a': 0
}, new Foo(), new Bar()));

We get {a: 1, b: 2, c: 3, d: 4} as the value logged from the console log because all of Foo and Bar instance variables have been added to the Foo and Bar instances as their own properties.

Conclusion

To implement the Lodash assign method, we just need to use the spread operator to merge the object’s own properties into the object that we want to have the properties merged in.

The assignIn method is more complex because we have to traverse up the prototype chain to merge in all those properties as well. We can do that by using a while loop with the Object.getPrototypeOf method.

Categories
Lodash

Learning JavaScript by Implementing Lodash Methods — Flattening and Mapping

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 create our own methods to flatten collections.

flatMap

The Lodash flatMap method runs an iteratee function on each array entry of the original array and then flattens the resulting array and returns it.

We can implement it as follows:

const flatMap = (arr, iteratee) => arr.map(iteratee).flat()

In the code above, we called the map method on arr to map each entry to iteratee then called flat to flatten the resulting array.

Then when we call our flatMap function as follows:

const result = flatMap([1, 2], a => [a, a]);

We get that result is [1, 1, 2, 2] .

flatMapDeep

Lodash’s flatMapDeep method is like flatMap , but recursively flattens the resulting array.

We can implement this as we did with the flatMap method as follows:

const flatMapDeep = (arr, iteratee) => arr.map(iteratee).flat(Infinity)

The only difference between our flatMapDeep and flatMap method is that we passed in Infinity to flat to recursively flatten our resulting array after mapping each entry by calling iteratee .

Then when we can it as follows:

const result = flatMapDeep([1, 2], a => [[a, a]]);

result should be [1, 1, 2, 2] .

flatMapDepth

The flatMapDepth is like flatMap but we can specify the depth of the flattening of the resulting array.

The plain JS flat method also accepts the depth of the flatten as an argument, so we can just use that again:

const flatMapDepth = (arr, iteratee, depth) => arr.map(iteratee).flat(depth)

Then when we call it as follows:

const result = flatMapDepth([1, 2], a => [
  [a, a]
], 1);

We get:

[
  [
    1,
    1
  ],
  [
    2,
    2
  ]
]

as the value of result since we specified that the depth is 1.

groupBy

The groupBy method returns an object that takes the items in an array, calls an iteratee function and add that as the key of the returned object].

Then it creates an array as the value for each key, and then push the items that are the same as the key when iteratee is called with it into the array.

We can implement that as follows:

const groupBy = (arr, iteratee) => {
  let obj = {};
  for (const a of arr) {
    if (!Array.isArray(iteratee(a))) {
      obj[iteratee(a)] = [];
    }
    obj[iteratee(a)].push(a);
  }
  return obj;
}

In the code above, we looped through the items with a for...of loop, then populate the keys by calling iteratee on an array entry and then add the key with the empty array as the value.

Then when push the items into the array of the corresponding key.

Then when we call it as follows:

const result = groupBy([6.1, 4.2, 6.3], Math.floor);

We get that result is:

{
  "4": [
    4.2
  ],
  "6": [
    6.3
  ]
}

includes

The Lodash includes method checks if an item is in the collection. It also takes a starting index to start searching for.

We can implement this with the slice and includes method as follows:

const includes = (arr, value, start) => arr.slice(start).includes(value)

In the code above, we called slice on arr to start the search from the start index. Then we call array instance’s includes method to check if the item is in the sliced array.

Then when we call it as follows:

const result = includes([6.1, 4.2, 6.3], 6.3, 1);

result should be true since 6.3 is in the part of the array that has an index bigger than or equal to 1.

invokeMap

The Lodash invokeMap method maps the values of an array and returns it according to what we pass in as the argument.

Lodash invokeMap takes a string with the function name or a function to invoke on each collection entry. It also takes an argument for arguments.

We can implement it as follows:

const invokeMap = (arr, fn, ...args) => arr.map(a => fn.apply(a, args))

In the code above, we used the rest operator to spread the args into an array. Then we called map on arr to map each entry by calling apply on fn to map the values with the specified function.

We kept it simple by only assuming that fn is a function, unlike the Lodash version.

Then when we call invokeMap as follows:

const result = invokeMap([123, 456], String.prototype.split, '')

We get that result is:

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

since we split the number into substrings.

Conclusion

The flatMap family methods can be implemented with the map and flat methods.

To implement the groupBy method, we just have to populate an object by calling the iteratee function on each array entry and then populating them as keys. The values will be the original values that return the same value as the key when we call iteratee on it.

We can use plain JavaScript’s includes method to implement our own Lodash’s includes .

Finally, invokeMap can be implemented by using the rest operator to spread the arguments into an array and then use apply to call the passed-in function on each entry.

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.