Массивы предоставляют множество методов. Чтобы упростить задачу, в этой главе они разделены на группы.
Мы уже знаем методы, которые добавляют и удаляют элементы с начала или с конца:
arr.push(...items)
– добавляет элементы в конец,
arr.pop()
– извлекает элемент с конца,
arr.shift()
– извлекает элемент с начала,
arr.unshift(...items)
– добавляет элементы в начало.
Вот еще несколько.
Как удалить элемент из массива?
Массивы являются объектами, поэтому мы можем попробовать использовать delete
:
let arr = ["Я", "иду", "домой"]; удалить arr[1]; // удаляем "идти" предупреждение(прибытие[1]); // неопределенный // теперь arr = ["I", , "home"]; оповещение(длина сообщения); // 3
Элемент был удален, но в массиве все еще есть 3 элемента, мы видим, что arr.length == 3
.
Это естественно, поскольку delete obj.key
удаляет значение по key
. Это все, что он делает. Для объектов нормально. Но для массивов мы обычно хотим, чтобы остальные элементы сместились и заняли освободившееся место. Мы ожидаем, что теперь у нас будет более короткий массив.
Поэтому следует использовать специальные методы.
Метод arr.splice — это швейцарский нож для массивов. Он может делать все: вставлять, удалять и заменять элементы.
Синтаксис:
arr.splice(start[, deleteCount, elem1, ..., elemN])
Он изменяет arr
начиная с индекса start
: удаляет элементы deleteCount
, а затем вставляет на их место elem1, ..., elemN
. Возвращает массив удаленных элементов.
Этот метод легко понять на примерах.
Начнем с удаления:
let arr = ["Я", "учусь", "JavaScript"]; arr.splice(1, 1); // из индекса 1 удаляем 1 элемент оповещение (прибытие); // ["Я", "JavaScript"]
Легко, правда? Начиная с индекса 1
удален 1
элемент.
В следующем примере мы удалим 3 элемента и заменим их двумя другими:
let arr = ["Я", "учусь", "JavaScript", "право", "сейчас"]; // удаляем 3 первых элемента и заменяем их другим arr.splice(0, 3, «Давай», «Потанцуем»); alert( arr ) // сейчас ["Давайте", "танцуем", "право", "сейчас"]
Здесь мы видим, что splice
возвращает массив удаленных элементов:
let arr = ["Я", "учусь", "JavaScript", "право", "сейчас"]; // удаляем 2 первых элемента пусть удалено = arr.splice(0, 2); предупреждение (удалено); // "Я", "учусь" <-- массив удаленных элементов
Метод splice
также позволяет вставлять элементы без каких-либо удалений. Для этого нам нужно установить для deleteCount
значение 0
:
let arr = ["Я", "учусь", "JavaScript"]; // из индекса 2 // удаляем 0 // затем вставляем «комплекс» и «язык» arr.splice(2, 0, «комплекс», «язык»); оповещение (прибытие); // «Я», «учусь», «сложный», «язык», «JavaScript»
Допускаются отрицательные индексы
Здесь и в других методах массивов допускаются отрицательные индексы. Они указывают позицию с конца массива, как здесь:
пусть arr = [1, 2, 5]; // от индекса -1 (один шаг от конца) // удаляем 0 элементов, // затем вставляем 3 и 4 arr.splice(-1, 0, 3, 4); оповещение (прибытие); // 1,2,3,4,5
Метод arr.slice намного проще, чем похожий на него arr.splice
.
Синтаксис:
arr.slice([начало], [конец])
Он возвращает новый массив, копируя в него все элементы от start
до end
индекса (не включая end
). И start
, и end
могут быть отрицательными, в этом случае предполагается позиция от конца массива.
Он похож на строковый метод str.slice
, но вместо подстрок создает подмассивы.
Например:
let arr = ["t", "e", "s", "t"]; предупреждение(arr.slice(1, 3)); // e,s (копируем от 1 до 3) предупреждение(arr.slice(-2)); // s,t (копируем от -2 до конца)
Мы также можем вызвать его без аргументов: arr.slice()
создает копию arr
. Это часто используется для получения копии для дальнейших преобразований, которые не должны влиять на исходный массив.
Метод arr.concat создает новый массив, включающий значения из других массивов и дополнительные элементы.
Синтаксис:
arr.concat(arg1, arg2...)
Он принимает любое количество аргументов — массивов или значений.
Результатом является новый массив, содержащий элементы из arr
, затем arg1
, arg2
и т. д.
Если аргумент argN
является массивом, то копируются все его элементы. В противном случае копируется сам аргумент.
Например:
пусть arr = [1, 2]; // создаем массив из: arr и [3,4] предупреждение(arr.concat([3, 4])); // 1,2,3,4 // создаем массив из: arr и [3,4] и [5,6] alert( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6 // создаем массив из: arr и [3,4], затем добавляем значения 5 и 6 alert( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6
Обычно он копирует только элементы из массивов. Остальные объекты, даже если они выглядят как массивы, добавляются целиком:
пусть arr = [1, 2]; пусть arrayLike = { 0: «что-то», длина: 1 }; предупреждение(arr.concat(arrayLike)); // 1,2,[объект Объект]
…Но если объект, подобный массиву, имеет специальное свойство Symbol.isConcatSpreadable
, то concat
обрабатывает его как массив: вместо этого добавляются его элементы:
пусть arr = [1, 2]; пусть arrayLike = { 0: «что-то», 1: «еще», [Symbol.isConcatSpreadable]: правда, длина: 2 }; предупреждение(arr.concat(arrayLike)); // 1,2,что-то,еще
Метод arr.forEach позволяет запускать функцию для каждого элемента массива.
Синтаксис:
arr.forEach(функция(элемент, индекс, массив) { // ... делаем что-то с элементом });
Например, здесь показан каждый элемент массива:
// для каждого элемента оповещение о вызове ["Бильбо", "Гэндальф", "Назгул"].forEach(alert);
И этот код более подробно описывает их позиции в целевом массиве:
["Бильбо", "Гэндальф", "Назгул"].forEach((элемент, индекс, массив) => { alert(`${item} находится по индексу ${index} в ${array}`); });
Результат функции (если она возвращает) отбрасывается и игнорируется.
Теперь давайте рассмотрим методы, выполняющие поиск в массиве.
Методы arr.indexOf и arr.includes имеют аналогичный синтаксис и делают по существу то же самое, что и их строковые аналоги, но работают с элементами, а не с символами:
arr.indexOf(item, from)
– ищет item
начиная с индекса from
, и возвращает индекс, в котором он был найден, в противном случае -1
.
arr.includes(item, from)
– ищет item
начиная с индекса from
, возвращает true
если найден.
Обычно эти методы используются только с одним аргументом: item
для поиска. По умолчанию поиск ведется с начала.
Например:
пусть arr = [1, 0, ложь]; предупреждение(arr.indexOf(0)); // 1 предупреждение(arr.indexOf(false)); // 2 предупреждение(arr.indexOf(null)); // -1 предупреждение(arr.includes(1)); // истинный
Обратите внимание, что indexOf
использует строгое равенство ===
для сравнения. Итак, если мы ищем false
, он находит именно false
, а не ноль.
Если мы хотим проверить, существует ли item
в массиве и не нужен ли индекс, то предпочтительнее использовать arr.includes
.
Метод arr.lastIndexOf аналогичен indexOf
, но ищет справа налево.
let Fruits = ['Apple', 'Orange', 'Apple'] alert(fruits.indexOf('Apple')); // 0 (первое яблоко) alert(fruits.lastIndexOf('Apple')); // 2 (последнее яблоко)
Метод includes
правильно обрабатывает NaN
.
Небольшая, но примечательная особенность includes
заключается в том, что он правильно обрабатывает NaN
, в отличие от indexOf
:
const arr = [NaN]; предупреждение(arr.indexOf(NaN)); // -1 (неверно, должно быть 0) alert( arr.includes(NaN) );// true (правильно)
Это связано с тем, что includes
было добавлено в JavaScript намного позже и внутри него используется более современный алгоритм сравнения.
Представьте, что у нас есть массив объектов. Как найти объект с определенным состоянием?
Здесь пригодится метод arr.find(fn).
Синтаксис:
let result = arr.find(function(item, index, array) { // если возвращается true, возвращается элемент и итерация останавливается // для ложного сценария возвращается неопределенное значение });
Функция вызывается для элементов массива последовательно:
item
— это элемент.
index
— это его индекс.
array
— это сам массив.
Если он возвращает true
, поиск останавливается, item
возвращается. Если ничего не найдено, возвращается undefined
.
Например, у нас есть массив пользователей, каждый из которых имеет поля id
и name
. Давайте найдем тот, у которого id == 1
:
пусть пользователи = [ {id: 1, имя: «Джон»}, {id: 2, имя: «Пит»}, {id: 3, имя: «Мэри»} ]; let user =users.find(item => item.id == 1); оповещение(имя_пользователя); // Джон
В реальной жизни массивы объектов — обычное дело, поэтому метод find
очень полезен.
Обратите внимание, что в примере мы find
функцию item => item.id == 1
с одним аргументом. Это типично, другие аргументы этой функции используются редко.
Метод arr.findIndex имеет тот же синтаксис, но возвращает индекс, в котором элемент был найден, а не сам элемент. Значение -1
возвращается, если ничего не найдено.
Метод arr.findLastIndex похож на findIndex
, но ищет справа налево, как и lastIndexOf
.
Вот пример:
пусть пользователи = [ {id: 1, имя: «Джон»}, {id: 2, имя: «Пит»}, {id: 3, имя: «Мэри»}, {id: 4, имя: «Джон»} ]; // Находим индекс первого Джона alert(users.findIndex(user => user.name == 'Джон')); // 0 // Находим индекс последнего Джона alert(users.findLastIndex(user => user.name == 'Джон')); // 3
Метод find
ищет один (первый) элемент, который заставляет функцию возвращать true
.
Если их много, мы можем использовать arr.filter(fn).
Синтаксис аналогичен find
, но filter
возвращает массив всех совпадающих элементов:
пусть результаты = arr.filter(function(item, index, array) { // если истинный элемент попадает в результаты и итерация продолжается // возвращает пустой массив, если ничего не найдено });
Например:
пусть пользователи = [ {id: 1, имя: «Джон»}, {id: 2, имя: «Пит»}, {id: 3, имя: «Мэри»} ]; // возвращает массив первых двух пользователей пусть someUsers =users.filter(item => item.id <3); предупреждение (someUsers.length); // 2
Перейдем к методам, преобразующим и переупорядочивающим массив.
Метод arr.map — один из самых полезных и часто используемых.
Он вызывает функцию для каждого элемента массива и возвращает массив результатов.
Синтаксис:
let result = arr.map(function(item, index, array) { // возвращает новое значение вместо элемента });
Например, здесь мы преобразуем каждый элемент в его длину:
let lengths = ["Бильбо", "Гэндальф", "Назгул"].map(item => item.length); предупреждение (длина); // 5,7,6
Вызов arr.sort() сортирует массив на месте , изменяя порядок его элементов.
Он также возвращает отсортированный массив, но возвращаемое значение обычно игнорируется, поскольку изменяется сам arr
.
Например:
пусть arr = [1, 2, 15]; // метод переупорядочивает содержимое arr арр.сорт(); оповещение (прибытие); // 1, 15, 2
Вы заметили что-нибудь странное в результате?
Порядок стал 1, 15, 2
. Неверно. Но почему?
По умолчанию элементы сортируются как строки.
Буквально все элементы преобразуются в строки для сравнения. Для строк применяется лексикографический порядок и действительно "2" > "15"
.
Чтобы использовать наш собственный порядок сортировки, нам нужно предоставить функцию в качестве аргумента arr.sort()
.
Функция должна сравнить два произвольных значения и вернуть:
функция сравнения(а, б) { если (a > b) вернуть 1; // если первое значение больше второго если (a == b) вернуть 0; // если значения равны если (a < b) вернуть -1; // если первое значение меньше второго }
Например, для сортировки по числам:
функция CompareNumeric(a, b) { если (a > b) вернуть 1; если (a == b) вернуть 0; если (a < b) вернуть -1; } пусть arr = [1, 2, 15]; arr.sort(compareNumeric); оповещение (прибытие); // 1, 2, 15
Теперь он работает так, как задумано.
Давайте отойдем в сторону и подумаем о том, что происходит. arr
может быть массивом чего угодно, верно? Он может содержать числа, строки, объекты или что-то еще. У нас есть набор некоторых предметов . Чтобы отсортировать его, нам нужна функция упорядочивания , которая умеет сравнивать его элементы. По умолчанию используется строковый порядок.
Метод arr.sort(fn)
реализует общий алгоритм сортировки. Нам не нужно заботиться о том, как это работает внутри (в большинстве случаев оптимизированная быстрая сортировка или Timsort). Он будет проходить по массиву, сравнивать его элементы с помощью предоставленной функции и переупорядочивать их. Все, что нам нужно, это предоставить fn
, который выполняет сравнение.
Кстати, если мы когда-нибудь захотим узнать, какие элементы сравниваются — ничто не мешает нам об этом предупредить:
[1, -2, 15, 2, 0, 8].sort(function(a, b) { Предупреждение(а + " <> " + b); вернуть а-б; });
Алгоритм может сравнивать один элемент с несколькими другими в процессе, но старается сделать как можно меньше сравнений.
Функция сравнения может возвращать любое число.
На самом деле функция сравнения требуется только для того, чтобы возвращать положительное число, чтобы сказать «больше», и отрицательное число, чтобы сказать «меньше».
Это позволяет писать более короткие функции:
пусть arr = [1, 2, 15]; arr.sort(function(a, b) { return a - b; }); оповещение (прибытие); // 1, 2, 15
Стрелочные функции для лучшего
Помните стрелочные функции? Мы можем использовать их здесь для более аккуратной сортировки:
arr.sort( (a, b) => a - b);
Это работает точно так же, как и более длинная версия выше.
Используйте localeCompare
для строк
Помните алгоритм сравнения строк? По умолчанию он сравнивает буквы по их кодам.
Для многих алфавитов лучше использовать метод str.localeCompare
для правильной сортировки букв, например Ö
.
Например, давайте разберем несколько стран на немецком языке:
пусть страны = ['Остеррайх', 'Андорра', 'Вьетнам']; alert(countries.sort( (a, b) => a > b ? 1 : -1) ); // Андорра, Вьетнам, Австрия (неверно) alert(countries.sort( (a, b) => a.localeCompare(b) ) ); // Андорра, Австрия, Вьетнам (верно!)
Метод arr.reverse меняет порядок элементов в arr
.
Например:
пусть arr = [1, 2, 3, 4, 5]; обр.реверс(); оповещение (прибытие); // 5,4,3,2,1
Он также возвращает массив arr
после разворота.
Вот ситуация из реальной жизни. Мы пишем приложение для обмена сообщениями, и человек вводит список получателей, разделенных запятыми: John, Pete, Mary
. Но для нас массив имен был бы гораздо удобнее, чем одна строка. Как это получить?
Метод str.split(delim) делает именно это. Он разбивает строку на массив по заданному разделителю delim
.
В приведенном ниже примере мы разделяем запятую, за которой следует пробел:
let name = 'Бильбо, Гэндальф, Назгул'; let arr = name.split(', '); for (пусть имя обр.) { alert(`Сообщение ${name}.`); // Сообщение Бильбо (и другим именам) }
Метод split
имеет необязательный второй числовой аргумент — ограничение на длину массива. Если он предусмотрен, то дополнительные элементы игнорируются. Однако на практике он используется редко:
let arr = 'Бильбо, Гэндальф, Назгул, Саруман'.split(', ', 2); оповещение (прибытие); // Бильбо, Гэндальф
Разбить на буквы
Вызов функции split(s)
с пустым s
разделит строку на массив букв:
пусть ул = "тест"; Предупреждение(str.split('')); // тест
Вызов arr.join(glue) выполняет обратный процесс вызову split
. Он создает строку элементов arr
, соединенных между собой glue
.
Например:
let arr = ['Бильбо', 'Гэндальф', 'Назгул']; пусть str = arr.join(';'); // склеиваем массив в строку с помощью ; оповещение (ул); // Бильбо;Гэндальф;Назгул
Когда нам нужно перебрать массив, мы можем использовать forEach
, for
или for..of
.
Когда нам нужно выполнить итерацию и вернуть данные для каждого элемента — мы можем использовать map
.
Методы arr.reduce и arr.reduceRight также относятся к этому типу, но они немного сложнее. Они используются для вычисления одного значения на основе массива.
Синтаксис:
let value = arr.reduce(function(аккумулятор, элемент, индекс, массив) { // ... }, [исходный]);
Функция применяется ко всем элементам массива один за другим и «переносит» свой результат на следующий вызов.
Аргументы:
accumulator
– результат предыдущего вызова функции, равен initial
в первый раз (если указан initial
).
item
– текущий элемент массива.
index
– это его позиция.
array
– это массив.
При применении функции результат предыдущего вызова функции передается следующей в качестве первого аргумента.
Итак, первый аргумент — это, по сути, аккумулятор, в котором хранится совокупный результат всех предыдущих выполнений. И в конце концов это становится результатом reduce
.
Звучит сложно?
Самый простой способ понять это — на собственном примере.
Здесь мы получаем сумму массива в одну строку:
пусть arr = [1, 2, 3, 4, 5]; let result = arr.reduce((sum, current) => sum + current, 0); оповещение (результат); // 15
Функция, передаваемая для reduce
использует только два аргумента, обычно этого достаточно.
Давайте посмотрим подробности происходящего.
При первом запуске sum
— это initial
значение (последний аргумент reduce
), равное 0
, а current
— это первый элемент массива, равный 1
. Таким образом, результат функции равен 1
.
При втором запуске sum = 1
мы добавляем к нему второй элемент массива ( 2
) и возвращаемся.
На третьем прогоне sum = 3
и мы добавляем к нему еще один элемент и так далее…
Ход расчета:
Или в виде таблицы, где каждая строка представляет вызов функции на следующем элементе массива:
sum | current | результат | |
---|---|---|---|
первый звонок | 0 | 1 | 1 |
второй звонок | 1 | 2 | 3 |
третий звонок | 3 | 3 | 6 |
четвертый звонок | 6 | 4 | 10 |
пятый звонок | 10 | 5 | 15 |
Здесь мы ясно видим, как результат предыдущего вызова становится первым аргументом следующего.
Мы также можем опустить начальное значение:
пусть arr = [1, 2, 3, 4, 5]; // удалено начальное значение из сокращения (нет 0) пусть результат = arr.reduce((сумма, ток) => сумма + ток); оповещение (результат); // 15
Результат тот же. Это потому, что если начального значения нет, то reduce
принимает первый элемент массива в качестве начального значения и начинает итерацию со второго элемента.
Таблица расчета такая же, как и выше, за исключением первой строки.
Но такое использование требует особой осторожности. Если массив пуст, то вызов reduce
без начального значения выдает ошибку.
Вот пример:
пусть arr = []; // Ошибка: сокращение пустого массива без начального значения // если бы начальное значение существовало, сокращение вернуло бы его для пустого массива. arr.reduce((сумма, ток) => сумма + ток);
Поэтому рекомендуется всегда указывать начальное значение.
Метод arr.reduceRight делает то же самое, но движется справа налево.
Массивы не образуют отдельный языковой тип. Они основаны на объектах.
Таким образом, typeof
не помогает отличить простой объект от массива:
предупреждение (тип {}); // объект предупреждение (тип []); // объект (тот же)
…Но массивы используются так часто, что для этого существует специальный метод: Array.isArray(value). Он возвращает true
если value
является массивом, и false
в противном случае.
оповещение(Array.isArray({})); // ЛОЖЬ предупреждение(Array.isArray([])); // истинный
Почти все методы массива, вызывающие функции — такие как find
, filter
, map
, за заметным исключением sort
, принимают необязательный дополнительный параметр thisArg
.
Этот параметр не описан в разделах выше, поскольку он используется редко. Но для полноты картины мы должны это раскрыть.
Вот полный синтаксис этих методов:
arr.find(func, thisArg); arr.filter(func, thisArg); arr.map(func, thisArg); // ... // thisArg — необязательный последний аргумент
Значение параметра thisArg
становится this
для func
.
Например, здесь мы используем метод объекта army
в качестве фильтра, и thisArg
передает контекст:
пусть армия = { мин.возраст: 18, Максимальный возраст: 27, canJoin (пользователь) { return user.age >= this.minAge && user.age < this.maxAge; } }; пусть пользователи = [ {возраст: 16}, {возраст: 20}, {возраст: 23}, {возраст: 30} ]; // находим пользователей, для которых Army.canJoin возвращает true пусть солдаты = пользователи.фильтр(армия.canJoin, армия); оповещение(солдаты.длина); // 2 оповещение(солдаты[0].возраст); // 20 предупреждение(солдаты[1].возраст); // 23
Если в приведенном выше примере мы users.filter(army.canJoin)
, то army.canJoin
вызывалась бы как отдельная функция с this=undefined
, что приводило бы к мгновенной ошибке.
users.filter(army.canJoin, army)
можно заменить users.filter(user => army.canJoin(user))
, который делает то же самое. Последний используется чаще, так как его легче понять большинству людей.
Шпаргалка по методам массива:
Чтобы добавить/удалить элементы:
push(...items)
– добавляет элементы в конец,
pop()
– извлекает элемент с конца,
shift()
– извлекает элемент с начала,
unshift(...items)
– добавляет элементы в начало.
splice(pos, deleteCount, ...items)
– по индексу pos
удаляет элементы deleteCount
и вставляет items
.
slice(start, end)
– создает новый массив, копирует в него элементы от start
до end
индекса (не включительно).
concat(...items)
– возвращает новый массив: копирует все члены текущего и добавляет в него items
. Если какой-либо из items
является массивом, то берутся его элементы.
Для поиска среди элементов:
indexOf/lastIndexOf(item, pos)
– ищет item
начиная с позиции pos
, и возвращает индекс или -1
, если не найден.
includes(value)
– возвращает true
если массив имеет value
, в противном случае — false
.
find/filter(func)
– фильтровать элементы через функцию, возвращать первые/все значения, которые заставляют ее возвращать true
.
findIndex
похож на find
, но возвращает индекс вместо значения.
Чтобы перебрать элементы:
forEach(func)
– вызывает func
для каждого элемента, ничего не возвращает.
Чтобы преобразовать массив:
map(func)
– создает новый массив из результатов вызова func
для каждого элемента.
sort(func)
– сортирует массив на месте, а затем возвращает его.
reverse()
— переворачивает массив на месте, а затем возвращает его.
split/join
– преобразовать строку в массив и обратно.
reduce/reduceRight(func, initial)
– вычислить одно значение в массиве, вызывая func
для каждого элемента и передавая промежуточный результат между вызовами.
Кроме того:
Array.isArray(value)
проверяет value
на предмет того, что оно является массивом, и если да, то возвращает true
, в противном случае — false
.
Обратите внимание, что методы sort
, reverse
и splice
изменяют сам массив.
Эти методы являются наиболее используемыми, они охватывают 99% случаев использования. Но есть и другие:
arr.some(fn)/arr.every(fn) проверяет массив.
Функция fn
вызывается для каждого элемента массива аналогично map
. Если какие-либо/все результаты true
, возвращается true
, в противном случае — false
.
Эти методы ведут себя примерно так ||
и операторы &&
: если fn
возвращает истинное значение, arr.some()
немедленно возвращает true
и прекращает перебор остальных элементов; если fn
возвращает ложное значение, arr.every()
немедленно возвращает false
и прекращает перебор остальных элементов.
Мы можем использовать every
для сравнения массивов:
функция arraysEqual(arr1, arr2) { return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]); } alert( arraysEqual([1, 2], [1, 2])); // истинный
arr.fill(value, start, end) – заполняет массив повторяющимся value
от start
до end
индекса.
arr.copyWithin(target, start, end) – копирует свои элементы от start
позиции до end
позиции в себя , в target
позиции (перезаписывает существующие).
arr.flat(глубина)/arr.flatMap(fn) создает новый плоский массив из многомерного массива.
Полный список смотрите в руководстве.
На первый взгляд может показаться, что методов так много, что их довольно сложно запомнить. Но на самом деле это гораздо проще.
Просмотрите шпаргалку, чтобы знать о них. Затем решите задачи этой главы на практике, чтобы у вас был опыт работы с методами массивов.
В дальнейшем, когда вам нужно что-то сделать с массивом, а вы не знаете как — приходите сюда, смотрите шпаргалку и находите правильный метод. Примеры помогут вам написать его правильно. Вскоре вы автоматически запомните методы, без особых усилий с вашей стороны.
важность: 5
Напишите функцию camelize(str)
, которая заменяет слова, разделенные тире, такие как «my-short-string», на «myShortString» в верблюжьем регистре.
То есть: удаляет все тире, каждое слово после тире становится заглавным.
Примеры:
Camelize("background-color") == 'backgroundColor'; Camelize("list-style-image") == 'listStyleImage'; Camelize("-webkit-transition") == 'WebkitTransition';
PS Подсказка: используйте split
, чтобы разделить строку на массив, преобразовать ее и снова join
.
Откройте песочницу с тестами.
функция Camelize(str) { возвратная ул. .split('-') // разбивает 'my-long-word' на массив ['my', 'long', 'word'] .map( // делает первые буквы всех элементов массива заглавными, кроме первого // преобразует ['my', 'long', 'word'] в ['my', 'Long', 'Word'] (слово, индекс) => индекс == 0 ? слово: word[0].toUpperCase() + word.slice(1) ) .присоединиться(''); // объединяет ['my', 'Long', 'Word'] в 'myLongWord' }
Откройте решение с тестами в песочнице.
важность: 4
Напишите функцию filterRange(arr, a, b)
, которая получает массив arr
, ищет элементы со значениями, большими или равными a
, и меньшими или равными b
и возвращает результат в виде массива.
Функция не должна изменять массив. Он должен вернуть новый массив.
Например:
пусть arr = [5, 3, 8, 1]; let filtered = filterRange(arr, 1, 4); оповещение(отфильтровано); // 3,1 (совпадающие значения) оповещение (прибытие); // 5,3,8,1 (не изменено)
Откройте песочницу с тестами.
функция filterRange(arr, a, b) { // добавили скобки вокруг выражения для лучшей читаемости return arr.filter(item => (a <= item && item <= b)); } пусть arr = [5, 3, 8, 1]; let filtered = filterRange(arr, 1, 4); оповещение(отфильтровано); // 3,1 (совпадающие значения) оповещение (прибытие); // 5,3,8,1 (не изменено)
Откройте решение с тестами в песочнице.
важность: 4
Напишите функцию filterRangeInPlace(arr, a, b)
, которая получает массив arr
и удаляет из него все значения, кроме тех, которые находятся между a
и b
. Тест: a ≤ arr[i] ≤ b
.
Функция должна только изменять массив. Он не должен ничего возвращать.
Например:
пусть arr = [5, 3, 8, 1]; filterRangeInPlace(arr, 1, 4); //удалены цифры кроме от 1 до 4 оповещение (прибытие); // [3, 1]
Откройте песочницу с тестами.
функция filterRangeInPlace(arr, a, b) { for (пусть я = 0; я <arr.length; i++) { пусть val = arr[i]; // удаляем, если вне интервала если (val <a || val > b) { arr.splice(я, 1); я--; } } } пусть arr = [5, 3, 8, 1]; filterRangeInPlace(arr, 1, 4); //удалены цифры кроме от 1 до 4 оповещение (прибытие); // [3, 1]
Откройте решение с тестами в песочнице.
важность: 4
пусть arr = [5, 2, 1, -10, 8]; // ... ваш код для сортировки по убыванию оповещение (прибытие); // 8, 5, 2, 1, -10
пусть arr = [5, 2, 1, -10, 8]; arr.sort((a, b) => b - a); оповещение (прибытие);
важность: 5
У нас есть массив строк arr
. Нам бы хотелось иметь его отсортированную копию, но оставить arr
без изменений.
Создайте функцию copySorted(arr)
которая возвращает такую копию.
let arr = ["HTML", "JavaScript", "CSS"]; пусть отсортировано = copySorted (arr); оповещение (отсортировано); // CSS, HTML, JavaScript оповещение (прибытие); // HTML, JavaScript, CSS (без изменений)
Мы можем использовать slice()
чтобы сделать копию и выполнить сортировку:
функция copySorted(arr) { вернуть arr.slice().sort(); } let arr = ["HTML", "JavaScript", "CSS"]; пусть отсортировано = copySorted (arr); оповещение (отсортировано); оповещение (прибытие);
важность: 5
Создайте функцию-конструктор Calculator
, которая создает «расширяемые» объекты калькулятора.
Задача состоит из двух частей.
Сначала реализуйте метод calculate(str)
, который принимает строку типа "1 + 2"
в формате «НОМБЕР-оператор НОМЕР» (с разделителями-пробелами) и возвращает результат. Надо понимать плюс +
и минус -
.
Пример использования:
пусть Calc = новый калькулятор; alert(calc.calculate("3 + 7")); // 10
Затем добавьте метод addMethod(name, func)
который обучает калькулятор новой операции. Он принимает name
оператора и функцию с двумя аргументами func(a,b)
которая его реализует.
Например, давайте добавим умножение *
, деление /
и степень **
:
пусть powerCalc = новый калькулятор; powerCalc.addMethod("*", (a, b) => a * b); powerCalc.addMethod("/", (a, b) => a / b); powerCalc.addMethod("**", (a, b) => a ** b); пусть результат = powerCalc.calculate("2 ** 3"); оповещение (результат); // 8
Никаких круглых скобок и сложных выражений в этом задании.
Числа и оператор разделяются ровно одним пробелом.
Если вы захотите добавить, может быть обработка ошибок.
Откройте песочницу с тестами.
Обратите внимание, как хранятся методы. Они просто добавляются в свойство this.methods
.
Все тесты и числовые преобразования выполняются в методе calculate
. В будущем он может быть расширен для поддержки более сложных выражений.
функция Калькулятор() { это.методы = { "-": (a, b) => a - b, "+": (a, b) => a + b }; this.calculate = функция (строка) { пусть Split = str.split(' '), а = +сплит[0], оп = разделить[1], б = +сплит[2]; if (!this.methods[op] || isNaN(a) || isNaN(b)) { вернуть НЭН; } вернуть this.methods[op](a, b); }; this.addMethod = функция (имя, функция) { this.methods[имя] = func; }; }
Откройте решение с тестами в песочнице.
важность: 5
У вас есть массив user
объектов, каждый из которых имеет user.name
. Напишите код, который преобразует его в массив имен.
Например:
let john = { name: "Джон", возраст: 25 }; let pete = { name: "Пит", возраст: 30 }; let mary = { name: "Мэри", возраст: 28 }; пусть пользователи = [Джон, Пит, Мэри]; пусть имена = /* ... ваш код */ предупреждение (имена); // Джон, Пит, Мэри
let john = { name: "Джон", возраст: 25 }; let pete = { name: "Пит", возраст: 30 }; let mary = { name: "Мэри", возраст: 28 }; пусть пользователи = [Джон, Пит, Мэри]; пусть имена = пользователи.карта(элемент => элемент.имя); предупреждение (имена); // Джон, Пит, Мэри
важность: 5
У вас есть массив user
объектов, каждый из которых имеет name
, surname
и id
.
Напишите код для создания из него еще одного массива объектов с id
и fullName
, где fullName
генерируется из name
и surname
.
Например:
let john = { name: «Джон», фамилия: «Смит», id: 1 }; let pete = { name: "Пит", фамилия: "Хант", id: 2 }; let mary = { name: "Мэри", фамилия: "Ключ", id: 3 }; пусть пользователи = [Джон, Пит, Мэри]; let userMapped = /* ... ваш код ... */ /* пользователиMapped = [ {fulName: "Джон Смит", идентификатор: 1}, {fulName: "Пит Хант", идентификатор: 2}, {fulName: "Мэри Ки", идентификатор: 3} ] */ предупреждение(userMapped[0].id) // 1 alert(usersMapped[0].fullName) // Джон Смит
Итак, на самом деле вам нужно сопоставить один массив объектов с другим. Попробуйте использовать =>
здесь. Есть небольшая загвоздка.
let john = { name: «Джон», фамилия: «Смит», id: 1 }; let pete = { name: "Пит", фамилия: "Хант", id: 2 }; let mary = { name: "Мэри", фамилия: "Ключ", id: 3 }; пусть пользователи = [Джон, Пит, Мэри]; пусть userMapped =users.map(user => ({ полное имя: `${user.name} ${user.surname}`, идентификатор: user.id })); /* пользователиMapped = [ {fulName: "Джон Смит", идентификатор: 1}, {fullName: "Пит Хант", идентификатор: 2}, {fulName: "Мэри Ки", идентификатор: 3} ] */ предупреждение (usersMapped[0].id); // 1 предупреждение (usersMapped[0].fullName); // Джон Смит
Обратите внимание, что в стрелочных функциях нам необходимо использовать дополнительные скобки.
Мы не можем написать так:
пусть userMapped =users.map(user => { полное имя: `${user.name} ${user.surname}`, идентификатор: user.id });
Как мы помним, есть две стрелочные функции: без value => expr
и со value => {...}
.
Здесь JavaScript будет рассматривать {
как начало тела функции, а не как начало объекта. Обходной путь — заключить их в «обычные» скобки:
пусть userMapped =users.map(user => ({ полное имя: `${user.name} ${user.surname}`, идентификатор: user.id }));
Теперь хорошо.
важность: 5
Напишите функцию sortByAge(users)
, которая получает массив объектов со свойством age
и сортирует их по age
.
Например:
let john = { name: "Джон", возраст: 25 }; let pete = { name: "Пит", возраст: 30 }; let mary = { name: "Мэри", возраст: 28 }; let arr = [Пит, Джон, Мэри]; сортировка по возрасту (обр); // сейчас: [Джон, Мэри, Пит] оповещение(обр[0].имя); // Джон оповещение(arr[1].имя); // Мэри оповещение(обр[2].имя); // Пит
функция sortByAge(arr) { arr.sort((a, b) => a.age - b.age); } let john = { name: "Джон", возраст: 25 }; let pete = { name: «Пит», возраст: 30 }; let mary = { name: "Мэри", возраст: 28 }; let arr = [Пит, Джон, Мэри]; сортировка по возрасту (обр); // теперь отсортировано так: [Джон, Мэри, Пит] оповещение(обр[0].имя); // Джон оповещение(arr[1].имя); // Мэри оповещение(обр[2].имя); // Пит
важность: 3
Напишите функцию shuffle(array)
, которая перемешивает (произвольно меняет порядок) элементы массива.
Несколько прогонов shuffle
могут привести к разному порядку элементов. Например:
пусть arr = [1, 2, 3]; перетасовать (аранж); //объем = [3, 2, 1] перетасовать (аранж); //объем = [2, 1, 3] перетасовать (аранж); //объем = [3, 1, 2] // ...
Все заказы элементов должны иметь равную вероятность. Например, [1,2,3]
можно переупорядочить как [1,2,3]
или [1,3,2]
или [3,1,2]
и т. д. с равной вероятностью в каждом случае.
Простым решением может быть:
функция перемешивания (массив) { array.sort(() => Math.random() - 0,5); } пусть arr = [1, 2, 3]; перетасовать (аранж); оповещение (прибытие);
В некоторой степени это работает, поскольку Math.random() - 0.5
— это случайное число, которое может быть положительным или отрицательным, поэтому функция сортировки переупорядочивает элементы случайным образом.
Но поскольку функция сортировки не предназначена для такого использования, не все перестановки имеют одинаковую вероятность.
Например, рассмотрим код ниже. Он выполняет shuffle
1000000 раз и подсчитывает появления всех возможных результатов:
функция перемешивания (массив) { array.sort(() => Math.random() - 0,5); } // количество появлений для всех возможных перестановок пусть счетчик = { '123': 0, '132': 0, '213': 0, '231': 0, '321': 0, '312': 0 }; для (let i = 0; i <1000000; i ++) { Пусть массив = [1, 2, 3]; Shuffle (массив); count [array.join ('')] ++; } // показать количество всех возможных перестановков для (let Key in count) { alert (`$ {key}: $ {count [key]}`); }
Пример результата (зависит от двигателя JS):
123: 250706 132: 124425 213: 249618 231: 124880 312: 125148 321: 125223
Мы можем ясно видеть предвзятость: 123
и 213
появляются гораздо чаще, чем другие.
Результат кода может варьироваться в зависимости от двигателей JavaScript, но мы уже можем видеть, что подход ненадежен.
Почему это не работает? Вообще говоря, sort
- это «черный ящик»: мы бросаем в него массив и сравнение и ожидаем, что массив будет отсортирован. Но из -за полной случайности сравнения, черный ящик сошел с ума, и от того, как именно он соходит с ума, зависит от конкретной реализации, которая отличается между двигателями.
Есть и другие хорошие способы выполнить задачу. Например, есть отличный алгоритм под названием Fisher-Yates Shuffle. Идея состоит в том, чтобы пройти массив в обратном порядке и поменять каждый элемент случайным перед ним:
Функция Shuffle (массив) { для (let i = array.length-1; i> 0; i--) { Пусть j = math.floor (math.random () * (i + 1)); // случайный индекс от 0 до i // Смена элементов массив [i] и массив [j] // мы используем синтаксис "Destructuring назначения" для достижения этого // вы найдете более подробную информацию об этом синтаксисе в последующих главах // то же самое можно написать как: // Пусть t = массив [i]; Array [i] = Array [j]; массив [j] = t [Array [i], Array [j]] = [Array [j], Array [i]]; } }
Давайте проверим это так же:
Функция Shuffle (массив) { для (let i = array.length-1; i> 0; i--) { Пусть j = math.floor (math.random () * (i + 1)); [Array [i], Array [j]] = [Array [j], Array [i]]; } } // количество выступлений для всех возможных перестановков Пусть count = { '123': 0, '132': 0, '213': 0, '231': 0, '321': 0, '312': 0 }; для (let i = 0; i <1000000; i ++) { Пусть массив = [1, 2, 3]; Shuffle (массив); count [array.join ('')] ++; } // показать количество всех возможных перестановков для (let Key in count) { alert (`$ {key}: $ {count [key]}`); }
Пример вывода:
123: 166693 132: 166647 213: 166628 231: 167517 312: 166199 321: 166316
Теперь выглядит хорошо: все перестановки появляются с той же вероятностью.
Кроме того, по теме производительности алгоритм Фишер-Йейтс намного лучше, накладных расходов «сортировки».
важность: 4
Напишите функцию getAverageAge(users)
, которая получает множество объектов с age
свойства и возвращает средний возраст.
Формула для среднего - (age1 + age2 + ... + ageN) / N
.
Например:
Пусть Джон = {имя: "Джон", возраст: 25}; Пусть pet = {name: "pete", возраст: 30}; Пусть Mary = {name: "mary", возраст: 29}; Пусть arr = [Джон, Пит, Мэри]; оповещение (getaverageage (arr)); // (25 + 30 + 29) / 3 = 28
Функция getaverageage (пользователи) { return users.reduce ((prev, user) => prev + user.age, 0) / users.length; } Пусть Джон = {имя: "Джон", возраст: 25}; Пусть pet = {name: "pete", возраст: 30}; Пусть Mary = {name: "mary", возраст: 29}; Пусть arr = [Джон, Пит, Мэри]; оповещение (getaverageage (arr)); // 28
важность: 4
Пусть arr
будет массивом.
Создайте функцию unique(arr)
, которая должна возвращать массив с уникальными элементами arr
.
Например:
функция unique(arr) { /* ваш код */ } Пусть строки = [«Заяц», «Кришна», «Зат», «Кришна», «Кришна», «Кришна», «Заяц», «Заяц», «:-О» ]; блюд (уникальный (строки)); // Заяц, Кришна, :-O
Откройте песочницу с тестами.
Давайте пройдемся по массивам:
Для каждого элемента мы проверим, есть ли в результирующем массиве этот элемент.
Если это так, то игнорируйте, в противном случае добавьте к результатам.
функция unique(arr) { пусть результат = []; для (пусть rAr) { if (! result.includes (str)) { result.push (str); } } результат возврата; } Пусть строки = [«Заяц», «Кришна», «Зат», «Кришна», «Кришна», «Кришна», «Заяц», «Заяц», «:-О» ]; блюд (уникальный (строки)); // Заяц, Кришна, :-O
Код работает, но в нем есть потенциальная проблема с производительностью.
Method result.includes(str)
Внутренне проходит result
массива и сравнивает каждый элемент с str
чтобы найти совпадение.
Таким образом, если в result
есть 100
элементов, и никто не совпадает с str
, он пройдет весь result
и сделает ровно 100
сравнений. И если result
велик, примерно 10000
, то будет 10000
сравнений.
Это сама по себе не проблема, потому что двигатели JavaScript очень быстрые, поэтому ходьба 10000
массив - это вопрос микросекунд.
Но мы делаем такой тест на каждый элемент arr
, в цикле for
.
Так что, если arr.length
составляет 10000
у нас будет около 10000*10000
= 100 миллионов сравнений. Это много.
Таким образом, решение полезно только для небольших массивов.
Далее в карте главы и набор мы увидим, как ее оптимизировать.
Откройте решение с тестами в песочнице.
важность: 4
Допустим, мы получили множество пользователей в форме {id:..., name:..., age:... }
.
Создайте функцию groupById(arr)
, которая создает из него объект, с id
в качестве ключа, а элементы массива - как значения.
Например:
Пусть пользователи = [ {id: 'John', имя: "Джон Смит", возраст: 20}, {id: 'ann', имя: "Энн Смит", возраст: 24}, {id: 'Pete', имя: "Пит Петерсон", возраст: 31}, ]; Let usersbyId = GroupById (пользователи); /* // после звонка мы должны иметь: usersbyId = { Джон: {id: «Джон», имя: "Джон Смит", возраст: 20}, Энн: {id: 'ann', имя: "Энн Смит", возраст: 24}, Пит: {id: 'Pete', имя: "Пит Петерсон", возраст: 31}, } */
Такая функция действительно удобна при работе с данными сервера.
В этой задаче мы предполагаем, что id
уникален. Там не может быть двух элементов массива с тем же id
.
Пожалуйста, используйте метод массива .reduce
в решении.
Откройте песочницу с тестами.
функция GroupByID (массив) { return array.reduce ((obj, value) => { obj [value.id] = value; вернуть OBJ; }, {}) }
Откройте решение с тестами в песочнице.