Урок 10

Практика: работа с данными

Собрали задачи, которые часто встречаются в реальной работе.

Группировка

function groupBy(items, keyFn) {
    return items.reduce((acc, item) => {
        const key = typeof keyFn === 'function' ? keyFn(item) : item[keyFn];
        acc[key] = acc[key] ?? [];
        acc[key].push(item);
        return acc;
    }, {});
}

groupBy(users, 'role');
groupBy(orders, (o) => o.status);

Индексация

function indexBy(items, key) {
    return items.reduce((acc, item) => {
        acc[item[key]] = item;
        return acc;
    }, {});
}

const usersById = indexBy(users, 'id');
usersById[1]; // user

Уникальные

const unique = [...new Set(items)];

function uniqueBy(items, key) {
    const seen = new Set();
    return items.filter((item) => {
        const k = typeof key === 'function' ? key(item) : item[key];
        if (seen.has(k)) return false;
        seen.add(k);
        return true;
    });
}

Сортировка

const sorted = [...users].sort((a, b) => a.age - b.age);

function sortBy(items, key, dir = 'asc') {
    return [...items].sort((a, b) => {
        const av = typeof key === 'function' ? key(a) : a[key];
        const bv = typeof key === 'function' ? key(b) : b[key];
        if (av < bv) return dir === 'asc' ? -1 : 1;
        if (av > bv) return dir === 'asc' ? 1 : -1;
        return 0;
    });
}

Слияние массивов объектов

const merged = [...a, ...b];

const byId = new Map();
for (const item of [...a, ...b]) {
    byId.set(item.id, item);
}
const deduped = [...byId.values()];

Обновление по id

const updated = users.map((u) =>
    u.id === targetId ? { ...u, name: 'Новое' } : u
);

Удаление по id

const filtered = users.filter((u) => u.id !== targetId);

Вставка в массив

const inserted = [...users.slice(0, index), newUser, ...users.slice(index)];

Перемещение

function move(arr, from, to) {
    const copy = [...arr];
    const [item] = copy.splice(from, 1);
    copy.splice(to, 0, item);
    return copy;
}

Подсчёт

const totals = orders.reduce((acc, o) => {
    acc[o.userId] = (acc[o.userId] ?? 0) + o.amount;
    return acc;
}, {});

Фильтр по нескольким условиям

const filtered = users.filter((u) =>
    (!filters.role || u.role === filters.role) &&
    (!filters.active || u.active === filters.active) &&
    (!filters.search || u.name.toLowerCase().includes(filters.search.toLowerCase()))
);

Пагинация

function paginate(items, page, perPage) {
    const start = (page - 1) * perPage;
    return {
        data: items.slice(start, start + perPage),
        total: items.length,
        page,
        lastPage: Math.ceil(items.length / perPage),
    };
}

Сортировка по нескольким полям

const sorted = [...users].sort((a, b) =>
    a.lastName.localeCompare(b.lastName) ||
    a.firstName.localeCompare(b.firstName)
);

Глубокое обновление

function updateNested(obj, path, value) {
    const [head, ...rest] = path;
    if (rest.length === 0) return { ...obj, [head]: value };
    return {
        ...obj,
        [head]: updateNested(obj[head] ?? {}, rest, value),
    };
}

Из массива в дерево

function buildTree(items, parentId = null) {
    return items
        .filter((item) => item.parentId === parentId)
        .map((item) => ({
            ...item,
            children: buildTree(items, item.id),
        }));
}

Итоги

  • groupBy, indexBy, uniqueBy
  • Сортировка с несколькими полями
  • Иммутабельные обновления
  • Map для дедупликации
  • Вставка через slice
  • Рекурсия для дерева