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.

Categories
JavaScript Basics

Sorting JavaScript Arrays

Sorting arrays is a common operation that’s done in many apps. The sort method that comes with JavaScript arrays makes this easy. However, there are lots of intricacies when it comes to sorting arrays.

In this article, we’ll look at how to sort arrays with the sort method. Also, we’ll look at how to compare objects to order them in the way that we desire.

Basic Sorting

The most basic sorting operation is probably sorting numbers. With the sort method, we can do this easily. For example, if we have the following array:

let arr = [3, 4, 5, 2, 5, 8, 9];

Then we can sort is with the sort method as follows:

arr.sort();

The arr array is sorted in place, so we don’t have to assign it to a new variable.

After calling sort, we get that the array is:

[2, 3, 4, 5, 5, 8, 9]

We can see that the sorting is done by ordering numbers in ascending order.

Sorting number arrays in ascending order are the simplest cases. We didn’t have to modify the sort method’s default functionality to do this.

We can also sort strings by ascending Unicode code point order. The sort method will sort by the total code points of each string. The full list of code points for Unicode characters are listed at https://en.wikipedia.org/wiki/List_of_Unicode_characters.

For example, given the following array:

let strings = ['foo', 'bar', 'baz'];

We can call sort by writing:

strings.sort();

to sort in ascending total Unicode code points.

Copying an Array Before Sorting

As we can see, sort will sort the array it’s called on in place. We can avoid this by copying the array to a new array before sorting.

To do this, we can either use the slice method as follows:

let sortedStrings = strings.slice().sort();

Or more conveniently, we can use the spread operator to do the same thing:

let sortedStrings = [...strings].sort();

Sorting with Compare Functions

The sort method can take an optional callback function to let us compare the content of the array to let us sort it the way we want.

The callback function takes 2 arguments. The first one is the array entry that comes first, the second is the one that comes after the one in the first argument.

We compare them and then return a positive number, negative number, or 0 depending on our preference.

Given that we have 2 entries, a and b for the first and second parameter for the callback respectively, we return a negative number to make a come before b. To make b come before a we can return a positive number.

To keep their order unchanged with respect to each other, we return 0.

For example, we can use this to sort numbers in reverse as follows:

arr.sort((a, b) => b - a);

We want to make the bigger entry come before the smaller entry, so we return b — a to make b come before a if b is bigger than a since b bigger than a means that b — a will be positive.

Otherwise, if b is smaller than a, it’ll be negative, and it’ll be zero if they’re the same.

It’s also handy for sorting by fields in an array of objects. If we want to sort by a field of objects, then we’ve to write a compare function to do it. For example, given the following array:

let arr = [{
    name: 'Joe',
    age: 20
  },
  {
    name: 'Mary',
    age: 16
  },
  {
    name: 'Jane',
    age: 22
  },
];

We can sort by the age field by ascending age as follows:

arr.sort((a, b) => a.age - b.age);

It works the same as sorting other numbers. If a.age is less than b.age , then we return a negative number, so a comes before b . If it’s the reverse, then b comes before a, where a and b are neighboring entries of the array.

Otherwise, if they’re the same, then a and b’s order stays the same.

Then we get the following if we log the array:

{name: "Mary", age: 16}
{name: "Joe", age: 20}
{name: "Jane", age: 22}

We can use the comparison operators like >, <, <=, or >=to compare strings.

For example, if we want to sort the array above by name, we can write:

arr.sort((a, b) => {
  if (a.name.toLowerCase() < b.name.toLowerCase()) {
    return -1;
  } else if (a.name.toLowerCase() > b.name.toLowerCase()) {
    return 1;
  }
  return 0;
});

Then we get the following result:

{name: "Jane", age: 22}
{name: "Joe", age: 20}
{name: "Mary", age: 16}

Comparing Non-English Strings

The callback for sorting strings above works great sorting English strings. However, it doesn’t work very well for non-English strings since it doesn’t take into account things like punctuation or accents.

To compare these kinds of strings, we can use the localeCompare method that comes with strings.

It has lots of optional that we can adjust for changing the comparison of strings, like whether to check accents, case, and other variants of a letter. The locale for comparison can also be adjusted.

The full list of options are at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare

We can use it as follows:

let arr = ['réservé', 'Premier', 'Cliché', 'communiqué', 'café', 'Adieu'];
arr.sort((a, b) => a.localeCompare(b, 'fr', {
  ignorePunctuation: true,
  sensitivity: 'base'
}));

In the code above, we passed a string b to compare with a in the comparison function of the localeCompare method as the first argument. Then we set the second argument to the 'fr' locale to indicate that we’re comparing French string. Finally, we passed in some basic options, which include ignoring punctuation and adjusting the sensitivity of the string comparison.

sensitivity set to 'base' means that strings that differ in letters are unequal.

After the sort method call above, we get that the value for arr is:

["Adieu", "café", "Cliché", "communiqué", "Premier", "réservé"]

Sorting JavaScript arrays have its tricky parts. The easiest case is sorting numbers and string code points in ascending order, which requires no comparison function.

For more complex comparisons, we’ve to pass in a comparison function. In it, returning a negative number will make the first value come before the second. Positive will make the second value come before the first. Returning 0 will keep the existing order.

Comparing non-English strings requires more care. We can use the localeCompare method that comes with strings to do the comparison by passing in various options.

Categories
JavaScript Basics

What Are Holes in Arrays?

One special feature of JavaScript arrays is that not every slot in the array has to be filled with values. This means that we can skip values as follows:

let arr = [];
arr[1] = 1;
arr[10] = 10;

We don’t have to worry about setting values for other slots of the array.

There’s also an alternative syntax for doing the above, by writing:

[, 1, , 2];

What we have above are called holes of an array, where we have nothing between the commas. An array with holes is called a sparse array.

In this piece, we’ll look at how holes in arrays are handled in JavaScript.


Checking for Values in Arrays

We can check for holes in arrays with the in operator. To do this, we can write something like the following code:

const arr = [, 1, , 2];
1 in arr;

We should get true returned from the last line since 1 is in the array.

ES6 treat holes in arrays as undefined entries. So if we want to check for holes in the array, check for undefined.

Iterators and generators also treat holes as undefined. For example, if we have

const arr = [, 1, , 2];
const iter = arr[Symbol.iterator]()

for (let a of iter) {
  console.log(a);
}

we get

undefined
1
undefined
2

If we call next to get the next item, as follows,

iter.next();

we get

{value: undefined, done: false}

for the first entry.

Likewise, if we have the given generator,

function* generator () {
  const arr = [, 1, , 2];
  for (let a of arr) {
    yield a;
  }
}

for (let a of generator()) {
  console.log(a);
}

we get the same thing.


Array.from()

Array.from() treats holes as undefined like with iterators and generators.

For example, if we have

const arr = [, 1, , 2];
const arrFrom = Array.from(arr);

then we get that the value of arrFrom is

[undefined, 1, undefined, 2]

Likewise, if we create an array from array-like objects, where we have non-negative integers as keys and a length property with a non-negative number as a value, missing entries are also treated as undefined.

For example, if we run

const arrFrom = Array.from({
  1: 'foo',
  length: 2
});

we get the value of arrFrom as

[undefined, "foo"]

How Array.prototype Methods Treat Holes

The behavior of these methods differs with different versions of JavaScript. In ES5, they’re the following:

  • forEach, filter, every, and some ignore holes.
  • map skips but preserves holes.
  • join and toString treat holes as if they were undefined elements but treat both null and undefined as empty strings.

The following is a full list of methods and how they deal with holes. Each method acts differently.

  • concat — keeps holes
  • copyWithin — holes are copied
  • entries, keys, values — treats holes as undefined
  • every — ignores holes
  • fill — fills holes
  • filter — removes holes
  • find — treats holes as elements
  • findIndex — treats holes as elements
  • forEach — ignores holes
  • indexOf — ignores holes
  • join — converts holes to empty strings
  • lastIndexOf — ignores holes
  • map — preserves holes
  • pop — treat holes as elements
  • push — preserves holes
  • reduce , reduceRight— ignores holes
  • reverse — preserves holes
  • shift — treat holes as undefined
  • slice — preserves holes
  • sort — preserves holes
  • toString — preserves holes
  • unshift — preserves holes
  • values — converts holes to undefined

When arrays are sparse, they have holes. They’re iterated through as undefined, but each array prototype method treats holes differently, so we have to be careful when defining arrays with holes and dealing with them.

We can use the in operator to check if a given entry is in an array.

Array.from() also treats holes as undefined and turns holes into undefined when converting arrays or array-like objects with holes into arrays.

Categories
JavaScript Basics

Truncating and Filling Entries: JavaScript Array Tips

JavaScript, like any other programming language, has many handy tricks that let us write our programs more easily. In this article, we will look at how to do different things that involve arrays, like truncating arrays, converting arrays to objects and filling arrays with data.

Truncating Arrays

In JavaScript, array objects have a length property that specifies the length of an array. It’s both a getter and setter property so in addition to being able to get the length of an array, we can also use it to set the length of an array.

Setting the length property to be less than the original length of the array will truncate the array up to the number of entries that we specify. For example, if we want to only keep the first entry of an array, then we can write something like:

let arr = [1, 2, 3, 4, 5, 6];
arr.length = 1
console.log(arr);

We simply set the length property of our array and then magically we get only the first element of the array left.

Alternatively, we can use the splice method to remove entries from an array. This method lets us removing, replace an existing element or adding new elements in place, but we only need it to delete elements from the array.

The argument of the splice method is the starting index start of which to start changing the array. It can be positive or negative. If it’s negative, then it’ll start changing the array from the end and move towards the start of the array. The end index is -1, the second is -2 and so on.

The second argument of the splice method is the deleteCount , which is an optional argument that lets us specify how many items to delete starting from the start parameter in the first element.

Subsequent arguments are the items that we want to insert into an array. This can go on for as long as we want. Only the first argument is required.

For example, if we want to truncate an array and keep only the first 2 elements, we can write something like the following code:

const arr = [1, 2, 3, 4, 5, 6];
arr.splice(2)
console.log(arr);

As we can see we only need to specify the first argument, and then the deleteCount will be automatically set so that it removes the entries on the right of the array. We can also specify a negative start index like in the following code:

const arr = [1, 2, 3, 4, 5, 6];
arr.splice(-1 * arr.length + 1)
console.log(arr);

So if we specify -1 * arr.length + n in the first argument, then we keep the first n entries of the original array.

We can also use the slice method to truncate arrays. It’s used for extracting values from an array in general and returns a new array with the extracted values. The slice method takes 2 arguments.

The first is an optional argument to specify where to begin extracting entries from an array. The second argument is another optional argument that specifies the end index of the original array to end the extraction of values from it. The value at the end index itself is excluded from the extraction.

For example, if we want the first 2 values from the original array, then we can write something like the following code:

let arr = [1, 2, 3, 4, 5, 6];
arr = arr.slice(0, 2);
console.log(arr);

When we run the code, we get back [1, 2] as the new value of arr . We can also specify negative indexes for the start and end. The last entry of the array is at -1, the second last element is at the index -2, and so on. So if we want to extract the first 2 elements of an array with negative indexes, we can write something like the following:

let arr = [1, 2, 3, 4, 5, 6];
arr = arr.slice(-1 * arr.length, -1 * arr.length + 2);
console.log(arr);

The first element of an array would be at index -1 * arr.length if we expressed it in terms of a negative index. Then the subsequent entries would be at -1 * arr.length + n where n is array entry at the n th position.

Filling Arrays with Data

With ES6 or later, we can fill arrays with an array with new data with the fill method. The fill method takes up to 3 arguments. The first is the value we want to add to the array.

The second optional argument is the start index of which we want to start filling data.

The default value of the second argument is 0. The third argument is an optional one that lets us specify the end index of to fill the array entry up to.

The default value for the third argument is the length of the array. Note that the end index itself is excluded from the filling so it won’t overflow when we call the fill method.

The fill the method returns a new array with the new values filled in. All existing values that were filled will replace any existing values that were in the original array.

For example, we can use it in the following way:

let arr = [1, 2, 3, 4, 5, 6];
arr = arr.fill(8)
console.log(arr);

Then we get the following output from the console.log :

[8, 8, 8, 8, 8, 8]

We can also change the index of the fill method to specify the fill boundary. For example, we can write:

let arr = [1, 2, 3, 4, 5, 6];
arr = arr.fill(8, 3, 5)
console.log(arr);

Then we get:

[1, 2, 3, 8, 8, 6]

from the console.log . If we specify an end index that’s over the length of the original array, then it’ll replace the values up to the original array up to the last position of the original array. For example, if we have:

let arr = [1, 2, 3, 4, 5, 6];
arr = arr.fill(8, 3, 10)
console.log(arr);

Then we get:

[1, 2, 3, 8, 8, 8]

We can also use negative indexes to specify the fill boundary with the fill method. The last position of the array would be -1, the second last would be -2, and so on. So we can call fill with negative indexes like the following:

let arr = [1, 2, 3, 4, 5, 6];
arr = arr.fill(8, -4, -1)
console.log(arr);

Then we get:

[1, 2, 8, 8, 8, 6]

since the third argument doesn’t include the last index. If we want to fill the last element as well with the new value, then we just omit the last argument like in the following code:

let arr = [1, 2, 3, 4, 5, 6];
arr = arr.fill(8, -4)
console.log(arr);

We get [1, 2, 8, 8, 8, 8] when we run the code above. If the second argument’s value is bigger than the third argument, then no fill operation will take place and the original array is returned. For example, if we have:

let arr = [1, 2, 3, 4, 5, 6];
arr = arr.fill(8, 4, 1)
console.log(arr);

Then we get back [1, 2, 3, 4, 5, 6] which is the original array.

Conclusion

In JavaScript, array objects have a length property that specifies the length of an array. It’s both a getter and setter property so in addition to being able to get the length of an array, we can also use it to set the length of an array. This means that setting the length property to be less than the original length of the array. We can also use the splice and slice methods to truncate arrays.

With ES6 or later, we can fill arrays with an array with new data with the fill method. The fill method takes up to 3 arguments. The first is the value we want to add to the array. The second optional argument is the start index of which we want to start filling data.

The default value of the second argument is 0. The third argument is an optional one that lets us specify the end index to fill the array entry up to. The default value for the third argument is the length of the array.

We can also use a negative index for the second and third arguments for setting the boundaries.

Categories
JavaScript Basics

Removing Duplicates and Emptying Arrays: JavaScript Array Tips

JavaScript, like any other programming language, has many handy tricks that let us write our programs more easily. In this article, we will look at how to do different things that involve arrays, like removing duplicates in an array and emptying an array.

Removing Duplicates in an Array

Using Sets

Removing duplicate entries from an array is a problem that we often have to solve there’re a few ways to do it. With ES6 or later, one of the easiest ways to do it is to convert it to a Set and then convert it back to an array.

We can convert an array to a Set by using the Set constructor. The constructor takes one argument, which is any iterable object. This means we can pass in an array, or other array-like objects like the arguments object, strings, NodeLists, TypedArrays like Uinit8Array, Map, other Sets and any other object that have a Symbol.iterator method.

We can also pass in null , which makes an empty set. Then we convert the set back to an array with the spread operator. For example, if we have an array, we can write the following code to convert it to a Set and back to an array to remove duplicate entries:

let arr = [1, 1, 2, 2, 3, 4, 5, 5, 6, 6];
arr = [...new Set(arr)];
console.log(arr);

In the code above, we started off with arr having duplicate entries and at the end, we should have the following logged:

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

Note that we use let in our declaration of our array. We need to use let so that we can assign the new array in the second back to our arr variable. The first occurrence of each entry is kept and the other repeat occurrences are discarded.

We can also use the Array.from method to convert a Set to an array. The first argument of the Array.from method is an array-like object, like the arguments object, strings, NodeLists, TypedArrays like Uinit8Array, Map, other Sets and any other object that has a Symbol.iterator method. This means that we can use it as an alternative to the spread operator inside an array-like in the following code:

let arr = [1, 1, 2, 2, 3, 4, 5, 5, 6, 6];
arr = Array.from(new Set(arr));
console.log(arr);

With the code above, we should get the same output as in the first example.

Using Array.filter Method

Another alternative way to remove duplicates from an array is to use an array’s filter method. The filter method lets us filter out array entries that already exists. It takes a callback function that has 2 parameters, the first one is an array entry and the second one is the index of the array that’s being iterated to by the filter method.

The filter method filter let us defined what we want to keep and return a new array with the entries kept. We can use it along with the indexOf method, which returns the first occurrence of an array entry.

The indexOf method takes in any object that we want to look up the index to. We can use it to filter out duplicate occurrences of array entries like in the following code:

let arr = [1, 1, 2, 2, 3, 4, 5, 5, 6, 6];
arr = arr.filter((entry, index) => arr.indexOf(entry) === index);
console.log(arr);

The callback function in the filter returns the condition of the array items that we want to keep. In the code above, we want to return arr.indexOf(entry) === index since we want to return the first occurrence of each entry only. arr.indexOf(entry) returns the index of the first occurrence of an entry and the index is the array entry that we want to check against as the filter method is iterating through it.

The first iteration of the filter method should have entry being 1 and index being 0, so we get:

arr.indexOf(1) === 0

which evaluates to true since indexOf gets the index of the first entry so it returns 0, so the filter method will keep it. In the next iteration, we have entry being 1 again and index being 1, so we get:

arr.indexOf(1) === 1

We already know that arr.indexOf(1) is 0 but the right side is 1. This means the expression will be evaluated to false , so the filter method will discard it. For the third entry, we have entry having the value 2, and index being 2. Then we get:

arr.indexOf(2) === 2

arr.indexOf(2) will return 2 since the first occurrence of 2 is 2, and we have 2 on the right side since index is 2, so the expression evaluates to true , so it’ll be kept in the returned array. This goes on until all the entries are iterated through and checked. Therefore, we should get the same entries as the other examples above.

Using Array.reduce Method

A third way to remove duplicate entries from an array is to use the reduce method. The method combines the entries of an array into one entity according to what we specify in the callback function that we pass in and returns the combined entity.

The reduce method takes in a function that has 2 parameters, the first parameter has array that entries that have the entries that were combined so far by the reduce method, and the second parameter has the entry that is to be combined into the first value in the first parameter.

The second argument is the initial value that the first parameter of the callback function will take on. For example, we can use it to filter out duplicate arrays like in the following code:

let arr = [1, 1, 2, 2, 3, 4, 5, 5, 6, 6];
arr = arr.reduce((noDupArr, entry) => {
  if (noDupArr.includes(entry)) {
    return noDupArr;
  } else {
    return [...noDupArr, entry];
  }
}, [])
console.log(arr);

In the code above, we pass in a callback function that checks if the noDupArr , which is an array without any duplicate values, includes the entry in the second parameter by using the includes method that comes with arrays. The includes method takes in any object and lets us check if it’s already inside noDupArr . Therefore, if noDupArr.includes(entry) is true , then we return whatever has been combined so far. Otherwise, if the value hasn’t been included in the array already, then we can append the entry , by using the spread operator to spread the existing entries of noDupArr into a new array, then we add entry as the last entry of the array.

With this logic, the subsequent occurrences of values after the first value won’t be included in the newly combined array. Therefore, we won’t have duplicate entries included in the array. Initially, noDupArr will be an entry as specified by the second argument of the reduce method.

Empty an Array

There are a few ways to empty an array. We can either set it to an empty array or set the array’s length to 0. For example, we can set it to an empty array like in the following code:

let arr = [1, 1, 2, 2, 3, 4, 5, 5, 6, 6];
arr = [];
console.log(arr);

The code above is the most obvious way to do it. A less known way to empty an array is to set its length to 0, as we do below:

let arr = [1, 1, 2, 2, 3, 4, 5, 5, 6, 6];
arr.length = 0;
console.log(arr);

Then we should also get an empty array for arr .

Conclusion

There’re many ways to remove duplicate entries from an array, each leaving the first occurrence of a value intact and discard the subsequent occurrences. We can convert it to a Set and then back to an array with the spread operator.

Also, we can use the filter and indexOf methods to return a new array with only the first occurrence of a value kept since indexOf returns the first occurrence of a value and the second parameter of the callback function that we pass into filter if the index of the original array that we can check against.

We can use the reduce and includes method together to do something similar to the filter and indexOf methods.

To empty arrays, we can set its length to 0 or set it to an empty array directly.