Javascript как оптимизировать эту функцию подсчета значений массива

#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 эквивалентен ассоциативному массиву в PHP

3. Нет, все, что я хотел, это оптимизированная версия моего исходного кода, которая похожа на функцию php, но если вы протестируете ее, она фактически возвращает массив, а не объект. Код работает нормально, я просто чувствовал, что это может быть быстрее.

4. Затем обертывание возвращаемого значения в Object.values делает то, что вы хотите. Позвольте мне добавить эту альтернативу