Categories
Lodash

Learning JavaScript by Implementing Lodash Methods — Transforming 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 object methods with plain JavaScript.

toPlainObject

The Lodash’s toPlainObject method converts an object to a plain object by merging in the inherited enumerable properties into the object itself.

We can implement it by using the Object.create method

Then we use the Object.getPrototypeOf method and then merge them into the object’s own properties.

For instance, we can implement it as follows:

const toPlainPObject = (obj) => {
  let plainObj = Object.create(null);
  plainObj = {
    ...obj
  };
  let prototype = Object.getPrototypeOf(obj)
  while (prototype) {
    plainObj = {
      ...plainObj,
      ...prototype
    }
    prototype = Object.getPrototypeOf(prototype)
  }
  return plainObj;
}

In the code above, we have our toPlainObject function, which takes the obj parameter containing the object that we want to convert to a plain object.

We then create a new object with Object.create with the argument null , which creates a plain object by creating an object without a prototype.

Then we use the spread operator to merge obj ‘s own properties into plainObj .

Next, we call Object.getPrototypeOf with obj passed in to get obj ‘s prototype. Then we use a while loop to merge the prototype’s properties into plainObj and go up the prototype chain with:

prototype = Object.getPrototypeOf(prototype)

In the end, we return plainObj .

Now when we call it as follows:

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

Foo.prototype.b = 2;
console.log(toPlainPObject(new Foo()));

We get the console log output is {a: 1, b: 2} .

toSafeInteger

Lodash’s toSafeInteger method converts a value to a safe integer. It does this by checking for Infinity and -Infinity and then return the maximum safe integer and the maximum safe integer multiplied by 1 respectively.

We can implement it as follows:

const toSafeInteger = val => {
  if (val === Infinity) {
    return Number.MAX_SAFE_INTEGER
  } else if (val === -Infinity) {
    return -1 * Number.MAX_SAFE_INTEGER
  }
  return isNaN(+val) ? 0 : Math.round(+val);
}

In the code above, we check if val is Infinity or -Infinity . If it’s Infinity , then we return Number.MAX_SAFE_INTEGER . If val is -Infinity , then we return -Number.MAX_SAFE_INTEGER .

Otherwise, we check if val can be converted to a number by calling isNaN . If it returns true then we return 0, otherwise, we call Math.round with +val after it’s converted to a number.

Then we can call it as follows:

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

Then we get the following output from each of the respectively:

3
0
9007199254740991
3

toString

Lodash’s toString method coverts a given value to a string. An empty string is returned for null and undefined values.

For instance, we can implement it as follows:

const toString = val => {
  if (Object.is(val, -0)) {
    return '-0'
  } else if (Object.is(val, +0)) {
    return '+0'
  } else if (Object.is(val, null) || Object.is(val, undefined)) {
    return ''
  } else if (Array.isArray(val)) {
    return val.join(',');
  }
  return JSON.stringify(val);
}

In the code above, we used Object.is to check whether val is one of the listed values. We use Object.is instead of === because -0 and +0 are considered different with Object.is .

If val isn’t -0 , +0 , null or undefined then we use JSON.stringify to convert the object to a string.

Then we can call it as follows:

console.log(toString(null));
console.log(toString(-0));
console.log(toString([1, 2, 3]));

Then we get an empty string from the first console log, '-0' from the 2nd console log and 1,2,3 from the last console log.

invert

The Lodash invert method flips the keys and values of an object. If 2 properties have the same value, then the later one overwrites the earlier one.

We can do that ourselves with the Object.keys method to get the keys to populate a new object with the keys and values inverted as follows:

const invert = (obj) => {
  let invertedObj = {};
  for (const key of Object.keys(obj)) {
    invertedObj[obj[key]] = key;
  }
  return invertedObj;
}

In the code above, we created the invertedObj object. Then we loop through the keys by getting them with the Object.keys method and then looping through them with the for...of loop.

Then we populate the values of obj as the keys of invertedobj and the keys of obj as the values of invertedObj .

In the end, when we call it as follows:

const object = {
  'a': 1,
  'b': 2,
  'c': 1
};

console.log(invert(object));

We get {1: “c”, 2: “b”} logged from the console log output.

Conclusion

To implement the toPlainObject method, we can use the Object.getPrototype method to traverse the prototype chain of an object.

The toString and toSafeInteger methods are similar in that they both do a series of checks on the argument passed in and then return some values accordingly.

Finally, we can use Object.keys to get the keys of an object to implement invert .

Categories
Lodash

Learning JavaScript by Implementing Lodash Methods — Loops 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 implement some Lodash methods that deal with iterating through and searching for data.

forEachRight

The forEachRight method lets us loop through a collection from end to start. It only loops through arrays.

We can implement it using a regular for loop as follows:

const forEachRight = (collection, iteratee) => {
  for (let i = collection.length - 1; i >= 0; i--) {
    const result = iteratee(collection[i])
    if (result === false) {
      break;
    }
  }
}

In the code above, we have a for loop that loops through our collection in reverse.

Inside the loop, we call iteratee on each item that’s being looped through.

Then if the returned value of the iteratee returns false , we break the loop.

We can then call it as follows:

forEachRight([1, 2], (value) => {
  if (value === 1) {
    return false;
  }
  console.log(value);
});

Once the value is 1, the loop ends, so we get 2 logged only.

every

The Lodash every method checks that a predicate returns truthy for all elements in a collection.

Since plain JavaScript has an every method that does the same thing but called on the array instance instead of passing in an array, we can implement the Lodash as follows:

const every = (arr, predicate) => arr.every(predicate)

Then when we call it as follows:

const result = every([1, 2, 3], x => x > 0);

We get that result is true since every element is bigger than 0.

filter

Lodash’s filter method iterates all elements of a collection and returns an array of all elements that return truthy.

JavaScript array has a filter method that does the same thing, so we can use it as follows:

const filter = (arr, predicate) => arr.filter(predicate)

The JavaScript filter method is part of the array instance, so we just call it to do the filtering for us.

Then when we call it as follows:

const result = filter([1, 2, 3], x => x > 1);

We get that result is [2, 3] since they’re bigger than 1.

find

find loops over a collection and returns the first item that meets the given condition.

The Lodash version of find also takes a starting index to search for.

We can implement our own Lodash find method with the plain JavaScript find method as follows:

const find = (arr, predicate, start) => arr.slice(start).find(predicate)

In the code above, we called slice with the start index to start searching from the start index with find .

Then we can call it as follows:

const result = find([4, 1, 2, 3], x => x > 1, 1);

And we get that result is 2 because it’s the first element that’s bigger than 1 and it’s index 1 or after.

findLast

The Lodash findLast method finds an element from right to left. The arguments are the same as find .

We can implement it with a for loop as follows:

const findLast = (arr, predicate, start = arr.length - 1) => {
  for (let i = start; i >= 0; i--) {
    if (predicate(arr[i])) {
      return arr[i]
    }
  }
}

In the code above, we looped through arr in reverse and call predicate on each entry until one returns a truthy value. If that’s the case, then that item is returned.

Then when we call it as follows:

const result = findLast([4, 1, 2, 3], x => x > 1, 1);

We get that result is 4 since that’s the first element in position less than or equal to 1 that’s bigger than 1.

Conclusion

The Lodash forEachRight method can be implemented with a for loop to loop through the items in reverse.

The every method can be implemented with plain JavaScript’s every method.

Likewise, the filter and find methods can be implemented with the plain JS version.

Finally, the findLast method can be implemented with a for loop that loop through items in reverse.

Categories
Lodash

Learning JavaScript by Implementing Lodash Methods — Arrays and 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 Lodash methods for zipping arrays into object properties and values, and looping through arrays and objects.

zipObject

The Lodash zipObject method props 2 arrays into object properties and values. The first array has the property names and the second has the property values.

We can implement our own zipObject method as follows:

const zipObject = (props, vals) => {
  let obj = {};
  for (let i = 0; i < props.length; i++) {
    obj[props[i]] = vals[i];
  }
  return obj;
}

In the code above, we create an empty object. Then we loop through the props array to get the property names and index.

We then use that to populate the obj object with our own properties and corresponding values by using the index to access the entries in the vals array.

Then when we call it as follows:

const result = zipObject(['a', 'b'], [1, 2]);

We then get the value for result :

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

countBy

The countBy method creates an object from an array that composes the array entries as the keys, where the keys are processed through an iteratee function. The value of each key is the count of the keys after they’ve been mapped through iteratee .

The iteratee takes one argument and maps the parameter to somethhing else.

We can implement it as follows:

const countBy = (arr, iteratee) => {
  let obj = {};
  for (const a of arr) {
    if (typeof obj[iteratee(a)] === 'undefined') {
      obj[iteratee(a)] = 0;
    } else {
      obj[iteratee(a)]++;
    }
  }
  return obj;
}

In the code above, we mapped the keys by calling iteratee with the key, which is the array entry.

Then we set the corresponding value to 0 when the property hasn’t been defined yet.

Otherwise, we increment the value by 1. In the end, we returnobj .

Then when we call it as follows:

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

We get that result is:

{
  "4": 0,
  "6": 1
}

forEach

The Lodash forEach method loops through an element in a collection and invokes iteratee on each element.

The iteratee function can stop the execution of the loop bu returning false explicitly.

It can take an array or an object as the first argument.

We can implement it as follows:

const forEach = (collection, iteratee) => {
  if (Array.isArray(collection)) {
    for (const a of collection) {
      const result = iteratee(a)
      if (result === false) {
        break;
      }
    }
  } else {
    for (const a of Object.keys(collection)) {
      const result = iteratee(collection[a], a)
      if (result === false) {
        break;
      }
    }
  }
}

In the code above, we first check if the collection is an array. If it is, then we use a for...of loop to loop through each entry and call iteratee on each entry.

If it returns false , then we stop the loop.

Likewise, if the collection isn’t an array, then we loop through the keys of the object by getting the keys with Object.keys and then run iteratee on each key-value pair.

The parameters of iteratee in the object case is the value and then the key, whereas the iteratee in the array case is just the array entry.

If iteratee returns false , then we stop the loop.

Then when we call it as follows:

forEach([1, 2], (value) => {
  if (value === 2) {
    return false;
  }
  console.log(value);
});

forEach({
  'a': 1,
  'b': 2
}, (value, key) => {
  console.log(key);
});

We get that the first forEach logs 1 and the second call logs ‘a’ and ‘b’.

Conclusion

The zipObject method can easily be implemented with the for loop and populating the keys and values into an object and return it.

The countBy method is similar, but the values are the count of each item, which is determined by first calling an iteratee function to populate the keys and count the number of items that appears after calling the iteratee function.

Finally, the forEach function can be implemented with the for...of loop and breaking the loop when the iteratee function returns false.

Categories
Lodash

Learning JavaScript by Implementing Lodash Methods — Diffing and Zipping

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 some methods to get the symmetric difference and zipping arrays.

xorBy

The xorBy method returns the symmetric difference of multiple arrays, which is an array without the entries that appear in all the arrays.

It does that by first running an iteratee function before doing the comparison. The iteratee function takes one argument to map a given entry to something else.

We can do that as follows:

const xorBy = (iteratee, ...arrs) => {
  const symDiff = [];
  for (const arr of arrs) {
    for (const a of arr) {
      const inAllArrays = arrs.every(arr => arr.map(iteratee).includes(iteratee(a)));
      if (!inAllArrays) {
        symDiff.push(a);
      }
    }
  }
  return symDiff;
}

In the code above, we used the rest operator to spread the arrays argument into an array of arrays.

Then we used the every method that’s in plain JavaScript to see if an entry is in all the arrays in our for...of loop.

When doing the comparison, we call map first with the iteratee function, then we also call iteratee when calling includes so that we’re comparing the mapped values instead of the original values.

Then if it’s not in the original array, then we put it in the symDiff array.

When we call it as follows:

const result = xorBy(Math.floor, [2.1, 1], [2.2, 3])

We get that result is [1, 3] because we mapped the values with Math.floor before comparing, so 2.1 and 2.2 are considered the same and are in all the arrays.

xorWith

xorWith is similar to xorBy in that, it runs a function. The difference is that the function is used to compare the values instead of mapping the values before comparing the items.

We can implement it similar to xorBy as follows:

const xorWith = (comparator, ...arrs) => {
  const symDiff = [];
  for (const arr of arrs) {
    for (const a of arr) {
      const inAllArrays = arrs.every(arr => arr.findIndex(b => comparator(a, b)) >= 0);
      if (!inAllArrays) {
        symDiff.push(a);
      }
    }
  }
  return symDiff;
}

In the code above, we used the every method like we did with xorBy , but we called findIndex with comparator inside to compare the values to determine if they’re in all the arrays.

If they’re not then we push them into the symDiff array. In the end, we return it.

Then when we call it as follows:

const result = xorWith((a, b) => Math.floor(a) === Math.floor(b), [2.1, 1], [2.2, 3])

We get tat result is:

[
  1,
  3
]

zip

The Lodash zip method groups elements, with the first elements of each array in the first array, the second element of each array in the second array, and so on.

We can do that we for loops as follows:

const zip = (...arrs) => {
  let zipped = [];
  for (let i = 0; i < arrs[0].length; i++) {
    if (!Array.isArray(zipped[i])) {
      zipped[i] = [];
    }
    for (const arr of arrs) {
      zipped[i].push(arr[i]);
    }
  }
  return zipped;
}

In the code above, we used the rest operator to spread the arrs argument into an array of arrays.

Then we loop through the items with a for loop and inside it, we check that if zipped[i] isn’t an array, then we create an array.

Once we did that we loop through the arrays in arrs with the for...of loop and get the array entry in the position i of each array and push it into the arrays in zipped array in the same position.

Then when we call zip as follows:

const result = zip(['a', 'b'], [1, 2], [true, false])

We see that result is:

[
  [
    "a",
    1,
    true
  ],
  [
    "b",
    2,
    false
  ]
]

Conclusion

We can get the symmetric difference in different ways with Lodash either by mapping the values with an iteratee function or by comparing them with the conparator function.

To zip an array of arrays, we create a new array by first creating a new array of arrays, then we push each entry of the original nested array in the given position into the new nested array in the same position.

Then we return that array.

Categories
Lodash

Plain JavaScript Versions of Lodash Methods to Slice and Join Arrays

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 replace the takeWhile , union , unionBy and unionWith with plain JavaScript code.

takeWhile

takeWhile is a reverse of takeRightWhile . It returns the slice of an array with elements taken from the beginning until the predicate function with the given array entry returns true .

We can easily implement this ourselves with slice as follows:

const takeWhile = (arr, predicate) => {
  let takenArr = [];
  for (const a of arr) {
    if (!predicate(a)) {
      takenArr.push(a);
    } else {
      return takenArr;
    }
  }
  return takenArr;
}

In the code above, we used the for...of loop to loop through the entries and returns takenArr if predicate(a) returns true .

We also return takenArr if the loop finishes if predicate(a) never returns true .

Then when we call it as follows:

const result = takeWhile([30, 40, 50], a => a > 40);

We get that result is [30, 40] since we specified we stop if an element if bigger than 40.

union

The union method returns a new array with unique values from multiple arrays in order using the SameValueZero equality comparison.

We can implement it ourselves with the spread operator as follows:

const union = (...arrs) => {
  return arrs.reduce((arr1, arr2) => [...new Set([...arr1, ...arr2])])
}

In the code above, we called reduce on arrs with a callback that returns the unique values of the 2 arrays spread with spread operator. The new array is then converted to a set and then converted back to an array with the spread operator.

Then we can call it as follows:

const result = union([30, 40, 50], [30, 60]);

and result is [30, 40, 50, 60] since the values from the earlier arrays are kept and then the later ones that aren’t already in the accumulated array is added.

unionBy

The unionBy method returns an array with the unique items in each array. The difference between this and union is that unionBy lets us pass in a method to map the values before doing the uniqueness comparison.

We can implement it as follows:

const unionBy = (iteratee, ...arrs) => {
  let unionArr = [];
  for (const arr of arrs) {
    const mapped = arr.map(iteratee);
    const unionMapped = unionArr.map(iteratee);
    const uniques = mapped.filter(a => !unionMapped.includes(a));
    unionArr = [...unionArr, ...uniques];
  }
  return unionArr;
}

In the code above, we have the iteratee as the first argument instead of the last so that we can use the rest operator with arrs to return an array of the array arguments passed in after the iteratee function.

Then we have a for...of loop to loop through the items and inside the loop, we map the arr entries with the iteratee function.

Also, we mapped the unionArr array with the same function so that we can use the includes method with filter to get the entries that aren’t unionArr yet after comparing it after converting the values with iteratee .

Then when we call it as follows:

const result = unionBy(Math.floor, [30.1, 40, 50], [30.2, 60]);

We get [30, 40, 50, 60] for result since we mapped the values in all arrays with Math.floor before doing a comparison.

unionWith

unionWith is like union except that it accepts a comparator to compare the elements and pick the first one that is unique.

We can implement our own unionWith function as follows:

const unionWith = (comparator, ...arrs) => {
  let unionArr = [];
  for (const arr of arrs) {
    const uniques = arr.filter(a => !unionArr.find(u => comparator(a, u)));
    unionArr = [...unionArr, ...uniques];
  }
  return unionArr;
}

In the code above, we looped through arrs with a for...of loop. Inside the loop, we called filter on arr to return an array with the ones that aren’t in the unionArr array and assign the array to the uniques constant.

Then we spread the values of the existing unionArr array and the uniques array and then assign that new array to unionArr .

Finally, we return unionArr . Then when we call it as follows:

const result = unionWith((a, b) => Object.is(a, b), [30, 40, 50], [30, 60]);

We get that result is [30, 40, 50, 60] since we compared the values with Object.is to determine if they’re unique or not.

Conclusion

The union , unionBy , and unionWith methods can be implemented with the spread operator and using array’s filter method.

takeWhile can be implemented with the for...of loop and checking for the given predicate in each iteration.