Сучасныя методы масэв JavaScript, якія заменяюць распашчатыя шаблоны коду
Дазвольце дазнацься, як новейшыя методы масэв JavaScript, такія як groupBy, toSorted, with і findLast, заменяюць слоўнасцовыя способы выкарыстоўвання reduce, spread і reverse.
Групаванне дадзейнаў без бібліятэкі: Object.groupBy() і Map.groupBy()
Працаваючы гады, каб раздзеліць масэвую структуру элементаў на катагорыі на аднойчынай праметры, трэба было пісаць сабе ўласны акумулятор reduce() або выкарыстоўваць lodash.groupBy толькі для гэтага заведама задання.
У спефікацыі ECMAScript тепер є вбудованыя механізмы групавання через Object.groupBy() і Map.groupBy().
Якую проблему гэта рашае
Калі вы отрымляеце простую колекцыю звярэнняў з API — транзакцыі, задачы, данні корыстнікаў і т.п. — часта патрэбна ўпорядакаваць іх у групы прытымкі перад адображэнням або выкарыстоўванням на іх базе бізнес-логікі.
Раней: рэалізацыя за дапамою reduce()
const inventory = [
{ name: 'Apples', type: 'fruit', quantity: 10 },
{ name: 'Bananas', type: 'fruit', quantity: 0 },
{ name: 'Carrots', type: 'vegetable', quantity: 14 },
{ name: 'Broccoli', type: 'vegetable', quantity: 5 }
];
const groupedByReduce = inventory.reduce((acc, item) => {
const key = item.type;
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(item);
return acc;
}, {});
Пазней: чысты, спецыяльна створаны вызов Object.groupBy
const inventory = [
{ name: 'Apples', type: 'fruit', quantity: 10 },
{ name: 'Bananas', type: 'fruit', quantity: 0 },
{ name: 'Carrots', type: 'vegetable', quantity: 14 },
{ name: 'Broccoli', type: 'vegetable', quantity: 5 }
];
const grouped = Object.groupBy(inventory, item => item.type);
/*
Output:
{
fruit: [
{ name: 'Apples', type: 'fruit', quantity: 10 },
{ name: 'Bananas', type: 'fruit', quantity: 0 }
],
vegetable: [
{ name: 'Carrots', type: 'vegetable', quantity: 14 },
{ name: 'Broccoli', type: 'vegetable', quantity: 5 }
]
}
*/
У замену выкарыстоўваеце Map.groupBy()
Калі ключ групавання — не проста строка чы сімбал, а болей складны об’ект чы дынамічны рэферэнс, Map.groupBy() ў такім случае яўляецца кращым выборам:
const vipTier = { tier: 'VIP' };
const standardTier = { tier: 'Standard' };
const customers = [
{ name: 'Alice', plan: vipTier },
{ name: 'Bob', plan: standardTier },
{ name: 'Charlie', plan: vipTier }
];
const groupedByPlan = Map.groupBy(customers, user => user.plan);
console.log(groupedByPlan.get(vipTier));
// Returns Alice and Charlie's records directly keyed by reference
Незменныя трансфармыціі, якім можна даверыць: toSorted(), toReversed(), toSpliced()
Класычная проблема ў JavaScript — це тое, што вядомыя методы .sort(), .reverse() і .splice() всі зменяюць масэвы на месцы, а не вяртаюць новы об’ект.
У фрэймворках, пабудаваных на прыглядным стацыянарным стане — React, Redux, Vue і падобных — безпасэчнае зміненне масэвы часта прыводзіць да тонкіх багоў і до ситуацый, калі відрасліванне элементаў тыхчэй выходзить з сінхроназаціі.
Якую проблему гэта рашае
Ёнколі ўбачаць гэтыя падступы, разработчыкі раней запісвалі захоўнія копіі спачатку, такія як [...array].sort() або array.slice().reverse(). Новая група методаў "змена массиву праз копію" цэлкам адмывае гэтыя зайвыя коды.
Порэшанне двух падходаў
// --- The Old Mutating Way ---
const scores = [88, 92, 79, 95];
const sortedMutated = scores.sort((a, b) => a - b);
console.log(scores); // [79, 88, 92, 95] -> Original data was destroyed!
// --- The Modern Non-Mutating Way ---
const originalScores = [88, 92, 79, 95];
const cleanSorted = originalScores.toSorted((a, b) => a - b);
console.log(originalScores); // [88, 92, 79, 95] -> Unchanged
console.log(cleanSorted); // [79, 88, 92, 95] -> Fresh array
Замена splice() на toSpliced()
toSpliced() дазволяе выдаліць, дадаць аб заменіць элементы на будзь-яй пазіцыі, не чапаючы заўсёды ўвесь первісны массив:
const tabs = ['Home', 'About', 'Pricing', 'Contact'];
// Remove 'Pricing' (index 2) and insert 'Services' & 'Blog'
const updatedTabs = tabs.toSpliced(2, 1, 'Services', 'Blog');
console.log(tabs); // ['Home', 'About', 'Pricing', 'Contact']
console.log(updatedTabs); // ['Home', 'About', 'Services', 'Blog', 'Contact']
Абнавленне аднае элемента без змены: with()
Якшчо вы хочаце зменіць значэнне на паведамленай індэксе, не чапаючы заўсёды заўсёды ўвесь первісны массив, звычайны падход — запісаць массив у копію, перазапісаць элемент на паведамленай індэксе і вернуць рызультат. with() адразу з’еднае ўсё гэта ў адну чытальную выраз.
Яка проблема рашыяецца
Пад час апдэйтаў масэвых данаёнаў часта патрабуецца заменіць адзін элемент па його пазычным номеру, заўсёды залікаючы абаковую масэ незменной.
Порэванне двух падходаў
const months = ['Jan', 'Mar', 'Mar', 'Apr'];
// Old way: Spread and mutate copy
const fixedMonthsOld = [...months];
fixedMonthsOld[1] = 'Feb';
// Modern way: array.with(index, value)
const fixedMonthsNew = months.with(1, 'Feb');
console.log(months); // ['Jan', 'Mar', 'Mar', 'Apr']
console.log(fixedMonthsNew); // ['Jan', 'Feb', 'Mar', 'Apr']
with() таксама падтрымлівае ад’ёмныя індэксы, таму замена элемента, які лічыцца ад канца, таксама ўсё проста:
const tags = ['v1.0.0', 'v1.1.0', 'v1.2.0-beta'];
const stableTags = tags.with(-1, 'v1.2.0');
console.log(stableTags); // ['v1.0.0', 'v1.1.0', 'v1.2.0']
Эфектывае пошук у спакою: findLast() і findLastIndex()
Аднаходжэнне элемента за дапамою .find() працуе нормальна, калі сканаваць даныя ад пачатку, але ён завжды пачынаецца з індэкса 0. Калі насправды патрабуецца апошнія знаходка, распашчальны спосаб — гэта ператворыць масэю у зворачаны стація, што вядзе да втраты часу і рызыку змены данаёнаў, якіх не хацелася чыгнуць.
Яка проблема рашыяецца
Даныя, такія як запісы журналу, історыя відкату, ленты актывацыі і записы транзакцый, па свайбе сортаваныя па часу, і частаючы вы хочаце побачыць найсвежэйшы запіс, які падпадае пад адзіныя з умоў.
Порэшчанне двух падходаў
const logs = [
{ id: 1, action: 'LOGIN', timestamp: 1000 },
{ id: 2, action: 'UPLOAD', timestamp: 1010 },
{ id: 3, action: 'ERROR', timestamp: 1020 },
{ id: 4, action: 'UPLOAD', timestamp: 1030 },
{ id: 5, action: 'LOGOUT', timestamp: 1040 }
];
// Old way: Reversing creates overhead and array allocation
const lastUploadOld = [...logs].reverse().find(log => log.action === 'UPLOAD');
// Modern way: findLast iterates backwards directly
const lastUpload = logs.findLast(log => log.action === 'UPLOAD');
const lastUploadIdx = logs.findLastIndex(log => log.action === 'UPLOAD');
console.log(lastUpload); // { id: 4, action: 'UPLOAD', timestamp: 1030 }
console.log(lastUploadIdx); // 3
Паколькі гэты падход скануе з канца і зупыняецца як толькі знаходзіць падходящы элемент, ён выконваецца за час O(k), дзе k — колькісць крокаў назад да падходящага элемента — не трэба спачатку копіюваць чы пертвараць весь масэвак.
5. Адрабатка Map і Filter адразу: flatMap()
Часта выкарыстоўваецца комбінацыя .map() з .filter() чы .flat(), калі кожны элемент выхіднага масэвака можа даць нуль, адзін чы калькі рэзультатаў. flatMap() з’едынае гэтыя каскады ў адну процедуру.
Што гэта рашыяе: гэта спакоюе стварэнне тымчасовых масэў, якія празьліваюцца пасля выкарыстоўвання, калі трэба перакаштаваць вярстакаваныя данні або фільтруваць і трансфармаваць іх у аднам кроку.
Прыклад: сплюшчэнне вярстака з вярстакаванымі элементамі
const authors = [
{ name: 'Author A', books: ['Book 1', 'Book 2'] },
{ name: 'Author B', books: ['Book 3'] },
{ name: 'Author C', books: [] }
];
// Map + Flat produces 2 array allocations
const booksNested = authors.map(a => a.books).flat();
// flatMap runs the map and flattens depth-1 in one pass
const allBooks = authors.flatMap(a => a.books);
console.log(allBooks); // ['Book 1', 'Book 2', 'Book 3']
Прыклад: адночасная трансфармацыя і фільтрацыя
Вы можете адмовіцца ад элемента, вярнуўшы порожній масэвак, і застацься з яным, вярнуўшы масэвак з адним элементам:
const rawInputs = ['42', 'invalid', '100', 'undefined', '256'];
const validNumbers = rawInputs.flatMap(input => {
const num = parseInt(input, 10);
return Number.isNaN(num) ? [] : [num];
});
console.log(validNumbers); // [42, 100, 256]
6. Чыстэйшы доступ да элементаў: at()
Для выявлення апошньага элемента масэвака традыцыйна выкарыстоўваліся array[array.length - 1], што є незручным і лёгка прыводзіць да памылак.
at() адбірае прыем паўнегатывых індэксаў, вядомы з Python, чым дазволяе спісваць назад ад канца масэвакоў і страк.
Да і пасля
const queue = ['Alice', 'Bob', 'Charlie', 'Dana'];
// Old way
const lastPersonOld = queue[queue.length - 1];
const secondLastOld = queue[queue.length - 2];
// Modern way
const lastPerson = queue.at(-1); // 'Dana'
const secondLast = queue.at(-2); // 'Charlie'
Рэзультат ўсьмохнейшы, менее схильны да памылак типу «одна единіца занадта» у вырахунавальных выразах, і лепа працюе з абэрнутымі вызывамі методаў, адколі не трэба спачатку зберагаць масэвую структуру ў тымчасовай зменнай.
Калі яго вжываць
Спіс перагляду для модернізацыі
- Перагляньце, як вы карыстаецеся станам: заменіце
.sort()і.reverse()на.toSorted()і.toReversed(), ўбачыць багі з тыхамі мутацый. - Адменшыце стандартныя функціі-редактары: вжывайце
Object.groupBy()замест аддатковых об’ектаў-акумулятораў. - Праспяшыце пошук: заменіце патэрны «спачатку адварот, потым пошук» на
findLast()для дадзеных у часовым порядку.
.at(-1) і .with(index, value) замест ручных вычыслаў дужыны і захоўніх копій масэв.Спаднёе чытанне
- Дзевяць распашчаёных шаблонаў, якіе спрычыняюць непатрэбныя перераскладанні React — Апаведамляе пра дзевяць распашчаёных шаблонаў стану і эфектаў у React, якія таямніча расшырваюць межы перераскладання, і як пераструктураваць компоненты, каб апдэйты застаўаліся локальнымі.
- Дзесяць схованых падступаў у компонентах React, якія спамляюць сучасныя прыкладненні — Дазволяе дакументавацца пра дзесяць распашчаёных памылак у компонентах React — ад недастатка семантычнага HTML да відсутнасці мемуязыі — і пра способы, якія неабходны, каб прыкладненні застаўаліся быстрымі, доступнымі і без бягаў у 2026 годзе.