Categories
JavaScript Basics

JavaScript Refactoring Tips — Arrays and Conditionals

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to write a piece of clean JavaScript code.

In this article, we’ll look at how to clean up conditionals with arrays, and use classList to manipulate class names.

Replacing Long Or Expressions with Array Checks

We can replace long or expressions with array checks easily. For instance, instead of writing:

if (fruit === 'apple' || fruit === 'orange' || fruit === 'grape') {
  //...
}

We can use some array methods to reduce the length of the conditional expression.

One way is to use the array instance’s includes method to do so:

if (['apple', 'orange' ,'grape'].includes(fruit)) {
  //...
}

As we can see, we checked if any one of them is the value of fruit by comparing the value of fruit in an array with includes .

The includes method returns true if the value we passed into the argument is included in the array instance and false otherwise.

Also, we can use the array instance’s some method to check the value:

if (['apple', 'orange', 'grape'].some(a => a === fruit)) {
  //...
}

The some method lets us check if there’s any array entry with the given condition in the callback exists.

It returns true if one or more exists and false otherwise.

We reduced the long conditional expression with arrays in the first example.

Using the classList Property in DOM Elements

The easiest way to check if a class exists in a DOM element and manipulate multiple classes is to use the classList property.

For instance, if we want to add multiple classes, we can write the following code:

const p = document.querySelector('p');
p.classList.add('foo');
p.classList.add('bar');
p.classList.add('baz');

This way, we can add multiple classes without manipulating strings. We just get the classList property of a DOM element object and then call add to add the classes by passing in a string with the class name into the the add method.

The rendered DOM element now has the foo , bar and baz classes.

Likewise, we can call the classList property’s remove method with a string with the class name to remove to remove the class.

For instance, we can write:

const p = document.querySelector('p');
p.classList.add('foo');
p.classList.add('bar');
p.classList.add('baz');
p.classList.remove('baz');

Then the rendered HTML only has the foo and bar classes since we called remove to remove the baz class.

To check if a class name exists in a DOM element object, we can use the contains method.

To use it, we can write:

const p = document.querySelector('p');
p.classList.add('foo');
p.classList.add('bar');
const hasBaz = p.classList.contains('baz');

In the code above, we called the contains method on the classList property to check if the baz class is rendered in the p element.

Since we didn’t add it in the HTML or with the add method, it should return false .

The classList property has the toggle method to toggle class names on and off. For instance, given the following HTML:

<button>
  toggle
</button>
<p>
  foo
</p>

We can use the button to toggle the bar class on and off as follows:

const p = document.querySelector('p');
const button = document.querySelector('button');
p.classList.add('foo');
p.classList.add('bar');

button.onclick = () => {
  p.classList.toggle('bar');
}

In the code above, we called the toggle method in the click handler to toggle the bar class on and off as we click the button. So we should see that in the HTML.

The clasList property has an array-like iterable object called the DOMTokenList object as a value. Therefore, we can use the spread operator to convert it to an array. This should convert it to an array of strings with the class names.

For instance, we can write the following code:

const p = document.querySelector('p');
p.classList.add('foo');
p.classList.add('bar');
const classArr = [...p.classList];

In the code above, we just used the spread operator to spread the classList property into an array.

Then we get that classArr is [“foo”, “bar”] .

Once we converted the DOMTokenList into an array, then we can use any array methods to manipulate the code.

The spread operator makes a copy of the original object and then spread it into an array, so we can still call the methods in the DOMTokenList to manipulate the class names as we want it.

Conclusion

Long boolean expressions with the || operator can be replaced with an array that checks if any of the items in the array matches the value that we’re checking for.

To manipulate multiple class names, we should use the classList property that’s part of a DOM element object. This way, we can add, remove, and toggle classes without having to manipulate strings and set it to the className property ourselves.

Categories
JavaScript Basics

Plain JavaScript of Lodash Array Methods

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 a few Lodash methods with plain JavaScript.

dropRightWhile

The dropRightWhile function lets us create a slice of an array starting from the right until a condition is met. The condition is determined by a callback.

We can implement it as follows with plain JavaScript as follows:

const dropRightWhile = (arr, condition) => {
  const newArr = [];
  for (let i = arr.length - 1; i >= 0; i--) {
    if (condition(arr[i])) {
      return newArr;
    }
    newArr.push(arr[i]);
  }
  return newArr;
}

In the code above, we have a for loop that’s looped through in reverse. If the condition function, which takes an array entry as the value returns true , then we return newArr , which has the items that are pushed into the array that we want to keep.

If the loop finishes without returning newArr , then it’ll also return newArr .

Therefore, when we have:

const arr = [1, 2, 3]
const dropped = dropRightWhile(arr, (a) => a === 2)

We get that dropped is [3] since we stopped the function when the callback condition is met, which is when an array entry is 2.

dropRightWhile can also take a string with the key name to check if it’s true before returning the sliced array.

We can add that check easily with the following code:

const dropWhile = (arr, key) => {
  const newArr = [];
  for (let i = arr.length - 1; i >= 0; i--) {
    if (arr[i][key]) {
      return newArr;
    }
    newArr.push(arr[i]);
  }
  return newArr;
}

Then when we have the following array and call it as follows:

const arr = [{
    name: 'foo',
    active: false
  },
  {
    name: 'bar',
    active: true
  },
  {
    name: 'baz',
    active: false
  }
]
const dropped = dropRightWhile(arr, 'active');

We get:

[
  {
    "name": "foo",
    "active": false
  }
]

since we return the array where we push the entries from the right until active is true .

dropWhile

dropWhile is the opposite of dropRightWhile . It gets a slice of an array by starting from the beginning and pushing those items into a new array, and returning it either when the loop finishes or when the condition in the callback returns true .

Like dropRightWhile , the callback takes an array entry. For instance, we can implement it ourselves as follows:

const dropWhile = (arr, condition) => {
  const newArr = [];
  for (const a of arr) {
    if (condition(a)) {
      return newArr;
    }
    newArr.push(a);
  }
  return newArr;
}

In the code above, we used the for...of loop instead of a regular for loop since we’re just looping through arr forward, which can be done easily with for loop.

dropWhile can also take a string with the key name to check if it’s true before returning the sliced array.

We can add that check easily with the following code:

const dropWhile = (arr, key) => {
  const newArr = [];
  for (const a of arr) {
    if (a[key]) {
      return newArr;
    }
    newArr.push(a);
  }
  return newArr;
}

Then when we have the following array and call it as follows:

const arr = [{
    name: 'foo',
    active: false
  },
  {
    name: 'bar',
    active: true
  },
  {
    name: 'baz',
    active: false
  }
]
const dropped = dropWhile(arr, 'active');

We get:

[
  {
    "name": "foo",
    "active": false
  }
]

since we return the array where we push the entries until active is true .

fill

The fill method fills the element of an array with values from the start but not up to the end index by replacing the values from the original array with the one that we provide starting from a starting index and ending with the end index.

We can implement fill as follows:

const fill = (arr, value, start, end) => arr.fill(valu, start, end)

In the code above, we have 4 arguments like the Lodash’s fill method, which are arr for the original array, value which is the value we want to fill the array with. start , which is the starting index of array entry we’re replacing, and end , which is the end index of the array we’re replacing the values with.

Then when we call the fill as follows:

const filled = fill([1, 2, 3, 4, 5], 8, 0, 3);

We get that filled is:

[8, 8, 8, 4, 5]

As we can see, the array instance’s fill method does the same job as Lodash’s fill .

Conclusion

We can implement dropWhile , dropRightWhile , and fill easily with plain JavaScript’s array methods. With a few lines of code, we eliminated the need to use Lodash array methods.

Categories
JavaScript Basics

ES6 String and Array Methods that Replace Older Methods

JavaScript is one of the most popular programming languages in the world.

This is because, since 2015, JavaScript has added lots of great features that are meant to replace old features from earlier versions.

In this article, we’ll look at ES6 string and array methods that replace earlier methods.


From indexOf to startsWith

ES6 introduced the string’s startsWith instance method that can be used as a replacement for the indexOf method.

For instance, given the following string:

const str = 'foobar';

Then we can replace:

str.indexOf('foo') === 0

With:

str.startsWith('foo')

If we want to check that the string str starts with 'foo'.

startsWith is much shorter than indexOf and we don’t have to check by index.


From indexOf to endsWith

The endsWith method is even more useful than the startsWith method since there was no easy way to check if a string ended with the given suffix.

Without endsWith, we had to write a function like the following:

const endsWith = (str, suffix) => {
  const index = str.indexOf(suffix);
  return index >= 0 &&
    index === str.length - suffix.length;
}

We had to check that the suffix was located in the string with str.indexOf(suffix) by checking that the index was 0 or larger. Then we had to check that the index was located at the end of the string by checking that it was in str.length — suffix.length.

Then given the following string:

const str = 'foobar';

We would write:

endsWith(str, 'bar')

With endsWith, we just write:

str.endsWith('bar')

It’s convenient that we don’t have to define our own function to check if a string ends with a given suffix string.


From indexOf to includes

The includes method introduced with ES6 checks that a string is located in a given string.

Therefore, given the following string:

const str = 'foobarbaz';

We can check if the substring exists in a string by writing:

str.indexOf('bar') >= 0

With ES6, we can instead write:

str.includes('bar')

This is shorter than indexOf.


From Array.prototype.indexOf to Array.prototype.findIndex

ES6 introduced the findIndex array instance method. It’s useful for finding the index of anything. It takes a callback that returns the condition of what we want to search for.

indexOf doesn’t work well with objects because it only finds the object if it’s the same reference as the given one.

For instance, indexOf works great with primitive values like numbers:

arr.indexOf(1)

It’s shorter than using findIndex as follows:

arr.findIndex(a => a === 1)

They both return the index of 1, which is 0.

However, when we’re searching for the index of an object, indexOf isn’t going to work. Given the following array:

If we write:

arr.indexOf({
  name: 'Alex'
})

Then we’ll get -1 because { name: ‘Alex’ } isn’t the same reference as the one in the array.

Therefore, findIndex will be useful here:

arr.findIndex(a => a.name === 'Alex')

This will return 1, which is the correct index because we return a condition that compares the value of the name property.


From Array.prototype.slice() to Array.from() or the Spread Operator

The slice and from instances can both be replaced with the spread operator in most cases.

For instance, we no longer need slice to convert array-like objects to arrays.

If we want to convert the arguments object into an array, instead of writing:

const arr = Array.prototype.slice.call(arguments);

We write:

const arr = [...arguments];

It’s much shorter and we don’t have to call call and pass in arguments as the this value.

In both examples, we get [1, 2, 3] assigned to arr when we have the given function:

function foo() {
  const arr = [...arguments];
}

And call it by writing:

foo(1, 2, 3);

We can also replace most use cases of the Array.from method with the spread operator.

As long as the object we pass into Array.from is an array or another kind of iterable object, we can use the spread operator with it.

Therefore, instead of writing:

function foo() {
  const arr = Array.from(arguments);
}

We can write:

function foo() {
  const arr = [...arguments];
}

The only case where Array.from is useful is when converting objects that have integers as their property identifiers and the length property to an array.

For instance, we can write:

To convert:

To [“foo”, “bar”, “baz”].

We can’t use the spread operator to convert that to an array since it’s not an iterable object.


Conclusion

Aside from a few edge cases, the new ES6 string and array functions are great additions that can be used to shorten code in many places.

The endsWith string instance method saves the most code. Shorter, self-explanatory code is easier on everyone’s brain.

Categories
JavaScript Basics

Plain JavaScript versions of Lodash Array Filtering and Manipulation Methods

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.

We can implement the pullAllBy , pullAllWith , pullAt , and remove methods easily with plain JavaScript.

pullAllBy

The pullAllBy returns an array that removes the given values that matches the items we want to remove after converting them with the iteratee function.

We can implement it as follows:

const pullAllBy = (arr, values, iteratee) => arr.filter(a => !values.map(iteratee).includes(iteratee(a)))

In the code above, we call the given iteratee function to map the values before doing the comparison with includes . Also in includes , we also call iteratee to do the same conversion so that they can be compared properly.

Then we can use our pullAllBy function as follows:

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

And we get [1] for result .

pullAllWith

The Lodash pullAllWith method takes a comparator instead of the iteratee to compare the values to exclude with the comparator instead of an iteratee to convert the values before comparing them.

For instance, we can implement it as follows:

const pullAllWith = (arr, values, comparator) => arr.filter(a => values.findIndex((v) => comparator(a, v)) === -1)

In the code above, we use the plain JavaScript’s findIndex method with a callback that calls the comparator to compare the values.

We call filter to filter out them items that are included with the values array.

Then when we call it as follows:

const result = pullAllWith([1, 2, 3], [2, 3], (a, b) => a === b)

We get [1] for result .

pullAt

The Lodash pullAt method returns an array with the elements with the items with the given indexes.

It also removes the elements in-place with those indexes.

Again, we can use the filter method to filter out the items that we want to remove as follows:

const pullAt = (arr, indexes) => {
  let removedArr = [];
  const originalLength = arr.length
  for (let i = 0; i < originalLength; i++) {
    if (indexes.includes(i)) {
      removedArr.push(arr[i]);
    }
  }

  for (let i = originalLength - 1; i >= 0; i--) {
    if (indexes.includes(i)) {
      arr.splice(i, 1);
    }
  }
  return removedArr.flat()
}

In the code above, we used a for loop to loop through the array and call splice on arr on the items with the given index in the indexes array.

Then we push the removed items into the removedArr array.

We then have and 2nd loop, which loops arr in reverse so that it doesn’t change arr ‘s indexes before we call splice on it.

In the loop, we just call splice to remove the items with the given index.

Finally, we call flat to flatten the array since splice returns an array.

Therefore, when we call it as follows:

const arr = [1, 2, 3]
const result = pullAt(arr, [1, 2])

We see that result is [2, 3] since we specified [1, 2] in the array with the index to remove and arr is now [1] since that’s the only one that’s not removed.

remove

The remove method removes the items from an array in place with the given condition and returns the removed items.

We can implement it ourselves with the for loop as follows:

const remove = (arr, predicate) => {
  let removedArr = [];
  const originalLength = arr.length
  for (let i = 0; i < originalLength; i++) {
    if (predicate(arr[i])) {
      removedArr.push(arr[i]);
    }
  }
  for (let i = originalLength - 1; i >= 0; i--) {
    if (predicate(arr[i])) {
      arr.splice(i, 1);
    }
  }
  return removedArr.flat();
}

In the code above, we have 2 loops as we have with the pullAt method. However, instead of checking for indexes, we’re checking for a predicate .

Like pullAt , we have to loop through the array indexes in reverse to avoid splice changing the indexes as we’re looping through the items of arr .

Then when we call remove as follows:

const arr = [1, 2, 3]
const result = remove(arr, a => a > 1)

We get that result is [2, 3] and arr is [1] since we specified that the predicate is a => a > 1 , so anything bigger than 1 is removed from the original array and the rest are returned.

Conclusion

The pullAt and remove methods are very similar, except that pullAt takes an array of index and remove takes a callback with the given condition.

pullAllBy and pullAllWith are implemented with the filter method. pullAllBy also needs to map the values with the iteratee function before doing comparisons.

Categories
JavaScript Basics

Manipulate JavaScript Arrays the Functional Way

Functional programming is a programming paradigm which states that we create computations as the evaluation of functions and avoid changing state and mutable data.

To write programs in a functional way, we use pure functions and immutable data structures.

In JavaScript, we can easily apply functional principles to arrays by using its built-in array methods.

In this article, we’ll look at the array methods that let us manipulate them in a functional way.

Filtering Items

We can use the filter method to return an array with entries from the original array that has the condition that the callback of the filter method returns.

For example, if we have the following array:

const store = [{
    name: "Apple",
    price: 1
  },
  {
    name: "Banana",
    price: 2
  },
  {
    name: "Grape",
    price: 1.2
  }
];

We can get the items that have price less than 2 as follows:

const cheapItems = store.filter(s => s.price < 2);

Then we get the following for cheapItems:

[
  {
    "name": "Apple",
    "price": 1
  },
  {
    "name": "Grape",
    "price": 1.2
  }
]

Using the filter method fits with the functional paradigm since the function is pure, given the same array and same condition, we always get the same results returned.

It also doesn’t change the existing elements of the array that it’s called on, which means we can’t accidentally change anything from the original array.

Mapping Array Objects

map is used to map entries of an array into something else. It’s frequently used to convert and transform data. Using map fits with the functional programming paradigm because again it’s a pure function that gives the same outputs for the given inputs, and it doesn’t change the original array.

As long as the function we pass into the map function to combine the values is pure, the map method should also be pure since it just calls this callback function and returns the updated values into a new array.

For example, given the following array:

const volumesInLiters = [{
    name: "Milk",
    volumeInLiters: 1
  },
  {
    name: "Apple Juice",
    volumeInLiters: 2
  },
  {
    name: "Orange Joice",
    volumeInLiters: 1.2
  }
];

We can add the volumeInQuarts field to each entry of the object and set the volume in quarts to as the value of each by writing:

const volumesInQuarts = volumesInLiters.map(v => {
  v = {
    ...v,
    volumeInQuarts: v.volumeInLiters * 1.057
  };
  return v;
})

We convert v.volumeInLiters * 1.057 and set it to volumeInQuarts for each entry.

Then we get:

[
  {
    "name": "Milk",
    "volumeInLiters": 1,
    "volumeInQuarts": 1.057
  },
  {
    "name": "Apple Juice",
    "volumeInLiters": 2,
    "volumeInQuarts": 2.114
  },
  {
    "name": "Orange Joice",
    "volumeInLiters": 1.2,
    "volumeInQuarts": 1.2684
  }
]

Using Reduce to Combine Values of Array Entries

Like filter and map, reduce also fits with the functional paradigm since we use it to gather entries of an array and return one value by the way that we specify.

As long as the function we pass into the reduce function to combine the values is pure, the reduce method should be pure. The function just calls the callback function we pass into reduce to compute the combined value.

For example, given the following array:

const store = [{
    name: "Apple",
    price: 1
  },
  {
    name: "Banana",
    price: 2
  },
  {
    name: "Grape",
    price: 1.2
  }
];

We can write the following to get the total price of all items:

const totalPrice = store.map(s => s.price).reduce((subtotal, b) => subtotal + b, 0);

We have to use map to map all the entries in the store array to price numbers. Then we can use the reduce method to add the new value to subtotal.

Then we should get 4.2 for totalPrice, which is the total of all the prices of the 3 items in store.

The second argument of reduce is the starting value of the reduction, or combining the values into one.

Conclusion

The filter, map, and reduce array methods are the main methods for manipulating arrays in a functional manner. They all return values and don’t change the array that it’s called on. This makes it difficult to unintentionally change the values.

filter returns an array that meets the condition that we return in the callback that we pass in.

map returns a new array that has the values of the original array mapped to new values in a way that we specify.

reduce combines the values of array entries into one by taking a callback function that we pass in to do the combining and returns the combined value.

The functions are pure if we pass in pure functions to them as callbacks since they just call the functions that we pass in to do the manipulation.