DEV Community

Cover image for New ECMAScript 23 array features
Matija Novosel
Matija Novosel

Posted on • Originally published at matijanovosel.com

New ECMAScript 23 array features

Introduction

Periodically, new features are introduced to Javascript - more accurately features are proposed with the ECMAScript standard then integrated into Javascript.

Over the years (just to name a few) we had ES6 which brought in the let and const keyword, ES2020 which had nullish coalescing, optional chaining etc.

The newest proposal, aptly named ES2023, has some new useful features concerning arrays we will get into shortly.

Array by copy

Some array methods mutate an array it was called upon, the most prominent of these being:

  • sort
  • splice
  • reverse

For example:

const x = [3, 2, 1];
const y = x.sort();

console.log(y); // [1, 2, 3] <- Ok
console.log(x); // [1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

Calling the sort method mutated the original array, which might not be neccessary if you planned on keeping the original array the same.

You can get around this if you spread the original array (only if the members of the array are primitive types):

const x = [3, 2, 1];
const y = [...x].sort();

console.log(y); // [1, 2, 3]
console.log(x); // [3, 2, 1]
Enter fullscreen mode Exit fullscreen mode

But why, if you could use a method for this? That's where the new to methods come into play.

In this instance, the toSorted method:

const x = [3, 2, 1];
const y = x.toSorted();

console.log(y); // [1, 2, 3]
console.log(x); // [3, 2, 1]
Enter fullscreen mode Exit fullscreen mode

The proposal is described here, with the other methods being:

  • toReversed
  • toSorted
  • toSpliced
  • with

Array grouping

As the name implies, this method groups its members together with a provided condition akin to the GROUP BY clause one can find in SQL.

Previously, a method that could be used to group elements together could be as follows:

/**
 * Groups values in an array of objects.
 * @param {any[]} array - The array of objects to be grouped by.
 * @param {string} property - The property of the objects to group by.
 * @return {any[]} Array of objects grouped by the provided property.
 */
export function groupBy(array, property) {
  return array.reduce((memo, x) => {
    if (!memo[x[property]]) {
      memo[x[property]] = [];
    }
    memo[x[property]].push(x);
    return memo;
  }, {});
}
Enter fullscreen mode Exit fullscreen mode

Let's say we have an array of employees, defined as such:

const employees = [
  { name: "Alina", company: "Google", id: 1 },
  { name: "Vika", company: "Coca Cola", id: 2 },
  { name: "Alex", company: "Jonson & Jonson", id: 3 },
  { name: "Vlad", company: "Google", id: 4 },
  { name: "Fibi", company: "Coca Cola", id: 5 },
  { name: "Joey", company: "Google", id: 6 }
];
Enter fullscreen mode Exit fullscreen mode

If we wanted to group them together by the company, we'd use the method as follows:

const grouped = groupBy(employees, "company");
Enter fullscreen mode Exit fullscreen mode

With the result being:

{
  Google: [
    {
      name: "Alina",
      id: 1
    },
    {
      name: "Vlad",
      id: 4
    },
    {
      name: "Joey",
      id: 6
    }
  ],
  "Coca Cola": [
    {
      name: "Vika",
      id: 2
    },
    {
      name: "Fibi",
      id: 5
    }
  ],
  "Jonson & Jonson": [
    {
      name: "Alex",
      id: 3
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

The same can now be achieved with the newly proposed methods, group and groupToMap.

The proposal is described here.

groupToMap

The groupToMap method groups the elements of the calling array using the values returned by a provided testing function. The final returned Map uses the unique values from the test function as keys, which can be used to get the array of elements in each group.

The method is primarily useful when grouping elements that are associated with an object, and in particular when that object might change over time. If the object is invariant, you might instead represent it using a string and group elements with group.

For example, let's say we have an array of game items:

const items = [
  { name: "potion", type: "consumable", price: 25 },
  { name: "sword", type: "weapon", price: 425 },
  { name: "shield", type: "protection", price: 225 },
  { name: "helmet", type: "protection", price: 125 }
];
Enter fullscreen mode Exit fullscreen mode

The code below uses groupToMap() with an arrow function that returns the object keys named unaffordable or affordable, depending on whether the element has a price lesser than 150. The returned result object is a Map so we need to call get() with the key to obtain the array.

const unaffordable = { unaffordable: true };
const affordable = { unaffordable: false };
const result = items.groupToMap(({ price }) =>
  price < 150 ? affordable : unaffordable
);
console.log(result.get(affordable));
// expected output: [{ name: "potion", price: 25 }, { name: "helmet", price: 125 }]
Enter fullscreen mode Exit fullscreen mode

group

The group method groups the elements of the calling array according to the string values returned by a provided testing function. The returned object has separate properties for each group, containing arrays with the elements in the group.

This method should be used when group names can be represented by strings.

Using the method on the array mentioned beforehand, we get a result as follows:

const result = items.group(({ type }) => type);
Enter fullscreen mode Exit fullscreen mode
{
  consumable: [
    {
      name: "potion",
      type: "consumable",
      price: 25
    }
  ],
  weapon: [
    {
      name: "sword",
      type: "weapon",
      price: 425
    }
  ],
  protection: [
    {
      name: "shield",
      type: "protection",
      price: 225
    },
    {
      name: "helmet",
      type: "protection",
      price: 125
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Array from async

The array method called from has been standardized and it is very useful for shallow copies of iterables or array like objects.

For example:

const letters = Array.from("hello"); // ["h", "e", "l", "l", "o"]
const squares = Array.from([4, 5, 6], (n) => n ** 2); // [16, 25, 36]
Enter fullscreen mode Exit fullscreen mode

It also provides a mapping callback that can be used on every member of the array.

However, a similar method does not exist for async iterables. Let's say we have a function that generates numbers asynchronously:

async function* asyncGen(n) {
  for (let i = 0; i < n; i++) yield i * 2;
}
Enter fullscreen mode Exit fullscreen mode

Without a dedicated function, one would create an array as follows:

const arr = [];
for await (const v of asyncGen(4)) {
  arr.push(v);
}
Enter fullscreen mode Exit fullscreen mode

The result being [0, 2, 4, 6]. Using the fromAsync method, an equivalent process can be done:

const arr = await Array.fromAsync(asyncGen(4));
Enter fullscreen mode Exit fullscreen mode

The proposal is described here.

Conclusion

Having looked at the newly proposed features, one can see that it is quite handy having them integrated into the default language features as you do not need to write your own implementations.

Top comments (0)