JavaScript · ES2024

ES15 Interactive Hub — ECMAScript 2024

Object.groupBy() for grouping data, Promise.withResolvers for event-driven code, Array.fromAsync, the RegExp v flag, String.isWellFormed/toWellFormed for UTF-16 safety, non-blocking Atomics.waitAsync — a richer functional + async toolkit.

Experience
0 XP
Course completion0%

Object.groupBy() & Map.groupBy()

Core topic

Medium6 min

💡 Concept summary

Group array elements by a key computed from a callback — replacing the clunky reduce + push pattern.

🤔 Why do you need this feature?

Group by is an extremely common data operation (by category, by date, by status...). Previously you had to write a reduce with an object accumulator yourself — long and error-prone. ES15 adds 2 standard static methods for this.

Before (old style)Verbose / clunky
// Before ES15 — use reduce yourself
var users = [
    { name: 'Lan', age: 25, role: 'dev' },
    { name: 'Nam', age: 30, role: 'pm' },
    { name: 'Hoa', age: 25, role: 'dev' },
];

var byRole = users.reduce(function(acc, u) {
    if (!acc[u.role]) acc[u.role] = [];
    acc[u.role].push(u);
    return acc;
}, {});

console.log(byRole);
// { dev: [Lan, Hoa], pm: [Nam] }
ES15 (Modern)Optimal & recommended
// ES15 — Object.groupBy & Map.groupBy
const users = [
    { name: 'Lan', age: 25, role: 'dev' },
    { name: 'Nam', age: 30, role: 'pm' },
    { name: 'Hoa', age: 25, role: 'dev' },
];

const byRole = Object.groupBy(users, u => u.role);
console.log(byRole);
// { dev: [Lan, Hoa], pm: [Nam] }

// Map version — preserves key order + accepts object/symbol as key
const byAge = Map.groupBy(users, u => u.age);
console.log(byAge.get(25)); // [Lan, Hoa]

// Group by range/condition
const byAdult = Object.groupBy([1, 5, 18, 22, 17], n =>
    n >= 18 ? 'adult' : 'minor'
);
Got it down? Try running real code or take the quiz!

ES15 Quick Reference

Quick syntax overview + copy snippets

Object.groupBy() & Map.groupBy()

Group array elements by a key computed from a callback — replacing the clunky reduce + push pattern.

const users = [
    { name: 'Lan', age: 25, role: 'dev' },
    { name: 'Nam', age: 30, role: 'pm' },
...
Promise.withResolvers()

Create a Promise and simultaneously obtain resolve/reject in an outer scope — no need to declare variables first then assign them inside the constructor.

const { promise, resolve, reject } = Promise.withResolvers();
setTimeout(() => resolve("Done!"), 500);
promise.then(console.log);
...
Array.fromAsync()

Create an array from an async iterable or an iterable containing promises — the async version of Array.from().

async function* gen() {
    yield 1; yield 2; yield 3;
}
...
RegExp v flag (Unicode Sets)

The "/v" flag for regex supports advanced set notation: intersection and subtraction between Unicode property sets.

const re = /[\p{L}--\p{ASCII}]/gv;
console.log('café Hello 中文'.match(re));
const han = /[\p{L}&&\p{Script=Han}]/gv;
...
String.isWellFormed() & toWellFormed()

A pair of methods to check and fix UTF-16 strings containing lone surrogates (broken Unicode characters) — important when receiving data from a DB/network with corrupted encoding.

const good = "Xin chào 👋"; // the emoji is a valid surrogate pair
const bad = "Hello\uD800World"; // lone high surrogate
console.log(good.isWellFormed()); // true
...
Atomics.waitAsync

The NON-BLOCKING version of Atomics.wait — waits for a shared memory value to change without freezing the thread. Important for the browser main thread (which is not allowed to block).

const sab = new SharedArrayBuffer(4);
const view = new Int32Array(sab);
const result = Atomics.waitAsync(view, 0, 0, 1000);
...