#javascript #node.js
#javascript #node.js
Вопрос:
У меня есть функция, которая имитирует array_count_values
функцию из php в javascript, но это не очень быстро. Мне интересно, есть ли способ это исправить?
function array_count_values(arr) {
let a = [], prev;
arr.sort();
for ( let i = 0; i < arr.length; i ) {
if ( arr[i] !== prev ) {
a.push(1)
} else {
a[a.length-1] ;
}
prev = arr[i];
}
return a;
}
Он просто возвращает простой массив чисел с таким количеством, как 2,1,2,1,1
. Входными данными в этом случае будут числовые массивы длиной 5-7 элементов, например array_count_values([6,4,10,6,6])
Комментарии:
1. Не будет ли лучше, если ваш результат будет таким,
object
гдеkeys
— значения массива, а объектvalues
— количество раз, когда появляется элемент?2. Отсутствие сортировки массива определенно поможет в работе.
3. Не в этом случае, так как мне нужно отсортировать его в порядке наибольшего сначала после того, как я получу результат от этой функции. Сортировать объект намного сложнее. Вы правы, обычно это так, и именно так оно и есть в php. Но в php такой объект сортировать проще.
4. Сортировка выполняется немного медленнее, поскольку она имеет временную сложность
O(nlogn)
. Это можно решить за один цикл.5. Да, я уверен, что функция не нуждается в сортировке. Мне нужен вывод массива, подобный этому, хотя
2,1,2,1,1
а не объект.
Ответ №1:
Вы можете использовать reduce
для перебора массива и подсчета каждой записи.
function array_count_values(arr) {
return arr.reduce((c, v) => {
c[v] = c[v] || 0;
c[v] ;
return c;
}, {})
}
var result = array_count_values([6, 4, 10, 6, 6]);
console.log(result);
Комментарии:
1. Это не то же самое, что мой код, он возвращает объект, а не массив. Мне просто нужна была оптимизированная версия моего кода.
Ответ №2:
Вы могли бы взять объект для подсчета и опустить сортировку. Этот подход использует один цикл.
function array_count_values(array) {
var count = {},
i;
for (i = 0; i < array.length; i ) {
if (array[i] in count) {
count[array[i]] ;
} else {
count[array[i]] = 1;
}
}
return Object.values(count).sort((a, b) => b - a);
}
console.log(array_count_values([6, 4, 10, 6, 6]));
Комментарии:
1. Обратите внимание, что это работает, потому что у вас одинаковые ссылки в массиве
[a, a]
, было бы[{ id: a }, { id: a }]
иначе.2. Все они возвращают объекты, в отличие от моего кода, который возвращает массив.
3. @Hasen, возвращая массив, вам также нужен отсортированный массив, иначе подсчеты не имеют смысла. вы хотите получить единый массив с подсчетами? какой порядок он должен иметь?
4. На данный момент я сортирую ее после получения массива из этой функции. Таким образом, он возвращает один массив, подобный
1,1,2,2,1
, а затем я сортирую его, чтобы получить2,2,1,1,1
. Порядок является первым по величине.5. затем просто добавьте сортировку в массив, подобный
result.sort((a, b) => b - a)
.
Ответ №3:
На самом деле это простой алгоритм. В последнее время я изучал их:
var array_count_values = function(array) {
let dict = {};
for (var i = 0; i < array.length; i ) {
var num = array[i];
(dict[num]) ? dict[num] : dict[num] = 1;
}
return dict;
}
console.log(array_count_values([6, 4, 10, 6, 6]));
Сложность во времени и пространстве — это и то, и другое O(n)
.
Ответ №4:
Я думаю, что добавление sort здесь является излишним и, вероятно, самой медленной частью этого.
Я думаю, что это будет самый быстрый / простой способ, которым вы можете это сделать.
function array_count_values(arr) {
let outputCounts = {};
for ( let i = 0; i < arr.length; i ) {
if (outputCounts[arr[i]] != undefined){
outputCounts[arr[i]] = 1;
} else {
outputCounts[arr[i]] = 1;
}
}
return outputCounts;
}
Предостережение здесь заключается в том, что вы собираетесь получить объект обратно вместо массива, как в вашем примере.
Комментарии:
1. Да, в этом проблема, он должен возвращать массив, как в моем примере кода, а не объект. Объекты могут быть приятными, но в некоторых ситуациях ими трудно манипулировать. В моей ситуации массив более полезен.
Ответ №5:
const arr = [1, 2, 2, 3];
function array_count_values (arr) {
const frequencies = arr.reduce((f, v) => {
const freq = f.get(v) || 0;
f.set(v, freq 1);
return f;
}, new Map());
return arr.map(v => frequencies.get(v));
}
console.log(array_count_values(arr));
Комментарии:
1. Не уверен, почему это было отклонено, это единственный ответ, который на самом деле возвращает массив, подобный моему коду, и как я указал. К сожалению, я протестировал ее, и, похоже, она работает примерно с той же скоростью, что и мой исходный код. Возможно, на несколько мс быстрее.
2. Я изначально неправильно реализовал свой ответ. Спасибо. Пожалуйста, обратите внимание, что вы знаете размер вашего ввода. Так что, возможно, вы могли бы попытаться написать действительно пользовательскую функцию без какого-либо цикла вообще. Эта реализация очень общая, было бы интересно посмотреть, как она масштабируется по сравнению с вашей реализацией.
Ответ №6:
Посмотрим, как array_count_values
работает в php. Это может быть то, что вы ищете
function array_count_values(arr) {
return arr.reduce((acc, val) => {
if (!acc[val]) {
acc[val] = 0
}
acc[val] = 1
return acc
}, {})
}
Чтобы вернуть массив, как требуется в вопросе
function array_count_values(arr) {
return Object.values(arr.reduce((acc, val) => {
if (!acc[val]) {
acc[val] = 0
}
acc[val] = 1
return acc
}, {}))
}
Комментарии:
1. Это возвращает объект, а не массив, как в моем примере кода?
2. Я предполагал, что вы хотели, чтобы она работала как
array_count_values
функция PHP, которая возвращает ассоциативный массив. Литерал объекта в JS эквивалентен ассоциативному массиву в PHP3. Нет, все, что я хотел, это оптимизированная версия моего исходного кода, которая похожа на функцию php, но если вы протестируете ее, она фактически возвращает массив, а не объект. Код работает нормально, я просто чувствовал, что это может быть быстрее.
4. Затем обертывание возвращаемого значения в
Object.values
делает то, что вы хотите. Позвольте мне добавить эту альтернативу