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.

Categories
Lodash

Learn JavaScript by Implementing Lodash Methods: Unique Array Entries

In this article, we’ll look at and learn more about JavaScript by implementing some Lodash methods in plain JavaScript, including uniq uniqBy , and uniqWith.


‘uniq’

The Lodash uniq method creates an array without duplicate values. Only the first occurrence of each value appears in the returned array.

Since JavaScript already has the set data structure, we can use that with the spread operator to remove all the duplicates from the original array and return it.

We can implement this as follows:

const uniq = arr => [...new Set(arr)]

In the code above, we just converted arr to a set to remove duplicate values and then converted it back to an array with the spread operator.

Then when we call it as follows …

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

… we get that result is [1, 2 ,3] .


'uniqBy'

uniqBy is like uniq except it takes an iteratee function, which is run before comparing the values for uniqueness. This means we can’t just convert it to a set. Instead, we have to run iteratee and then compare them to the existing entries in the array we return.

To do that, we implement the uniqBy in the following way:

const uniqBy = (arr, iteratee) => {
  let uniques = [];
  for (let a of arr) {
    const mappedUniques = uniques.map(iteratee);
    if (!mappedUniques.includes(iteratee(a))) {
      uniques.push(a);
    }
  }
  return uniques;
}

In the code above, we have the uniques array, which will have the array of unique entries that we’ll return.

Then we loop through arr with the for...of loop. Inside the loop, we map the uniques array with iteratee to get the mapped values.

Next, we check if each array entry is already in uniques by checking if it’s in the mappedUniques array instead of uniques since we want to compare the uniqueness after it’s been converted by the iteratee function.

If it’s not included by checking with the includes method, then we push the value into uniques.

Once the loop is finished, we return uniques. Then when we call it as follows:

const result = uniqBy([1, 2, 2.1, 3], Math.floor);

Then result is [1, 2, 3] since 2 and 2.1 are the same after calling Math.floor on them. iteratee can be any function that takes one argument and returns something derived from it.


'uniqWith'

uniqWith is like uniq except it accepts a comparator that’s run to compare the existing values that have unique values with the ones that doesn’t.

If the entry in the original array isn’t in the array with unique values, then we put it into the array with the unique values.

The array with unique values is returned in the end after checking all the values in the given array.

We can implement it as follows:

const uniqWith = (arr, comparator) => {
  let uniques = [];
  for (let a of arr) {
    if (uniques.findIndex(u => comparator(a, u)) === -1) {
      uniques.push(a);
    }
  }
  return uniques;
}

In the code above, we have:

uniques.findIndex(u => comparator(a, u)) === -1

This checks to see if the items in uniques are the same as the array entry that we’re looping through. If it isn’t (as indicated by the return value -1), then we put it in uniques.

When the loop is done, we return uniques.

Then we run it as follows:

const result = uniqWith([1, 2, 2.1, 3], (a, b) => Math.floor(a) === Math.floor(b));

From this, we get [1, 2, 3] as the value of result since our comparator determines that if the floor of both values is the same, then they’re the same. The comparator function has to take two arguments with the two values to compare.


Conclusion

The uniq Lodash method can easily be implemented with plain JavaScript sets and the spread operator

uniqBy is a bit harder to implement since we need to map the values with the iteratee function that’s passed in before we can compare them for uniqueness.

uniqWith is similar to uniqBy in that we have to run the comparator function to compare the values to determine if the items are unique.

Categories
Lodash

Plain JavaScript Versions of Lodash Methods to Reverse and Sort 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 reverse , slice , tail, takrRight, takeRightWhile and take methods with plain JavaScript code.

reverse

The Lodash reverse method reverses an array so that the first element becomes the last 2nd becomes 2nd last and so on.

This method is based on Arrar.prototype.reverse so that we don’t need to use Lodash’s reverse to reverse arrays.

We can just implement Lodash’s reverse method as follows:

const reverse = arr => arr.reverse()

Then we can call it as follows:

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

and we get that result is [3, 2, 1] .

slice

Lodash’s slice method returns a slice of an array from the beginning index up to the end index. We can specify the start and end index.

It’s based on Array.prototype.slice so we don’t need to use it. If we really want Lodash’s slice method, we can implement it as follows:

const slice = (arr, start, end) => arr.slice(start, end)

We just called the arr ‘s slice method with the start and end index.

We can call it as follows:

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

and we get that result is [2] .

tail

The tail method returns an array that has all but the first element of the array.

We can implement it as follows:

const tail = (arr) => arr.slice(1)

In the code above, we just called slice with 1 to exclude the first element

We can call it as follows:

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

Then result is [40, 50] .

take

take returns a slice of an array with n elements are taken from the beginning

Again, we can implement with slice as follows:

const take = (arr, n) => arr.slice(n)

Passing n as the first argument of slice returns an array with the first n entries removed.

We can call it as follows:

const result = take([30, 40, 50], 2);

Then we get that result is [50].

takeRight

The takeRight method returns a slice of an array with the n elements removed from the end.

Once again, we can use slice as follows:

const takeRight = (arr, n) => arr.slice(0, -n)

In the code above, we passed in 0 and -n to slice so that we return an array with from the first to the nth last entry inside.

Then when we call it as follows:

const result = takeRight([30, 40, 50], 2);

we get that result is [30] .

takeRightWhile

takeRightWhile returns a new array with the entries taken from the original array from the right until the given predicate that we used to end the method by returning the array returns true .

We can implement it as follows:

const takeRightWhile = (arr, predicate) => {
  let takenArr = [];
  for (let i = arr.length - 1; i >= 0; i--) {
    if (!predicate(arr[i])) {
      takenArr.push(arr[i]);
    } else {
      return takenArr.reverse();
    }
  }
  return takenArr.reverse();
}

In the code above, we looped through arr in reverse with a for loop. Then we run the predicate function in each iteration with each element as the argument and see if it returns true .

If it doesn’t then we push the entry into takenArr . Otherwise, we return takenArr reversed so that we have the elements in the original order.

Then we can call it as follows:

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

and result is [40, 50] since we specifies that we stop if the first entry called with the predicate returns true .

Conclusion

The takeRightWhile method can easily be implemented with simple loops. We just have to loop through items in reverse instead of forward to get items from the end first and work towards the start.

The takeRight, tail and take methods can be implemented with slice .

reverse and slice methods are already built into plain JavaScript and it’s with Lodash uses anyways, so we can just use the plain JavaScript versions.