Создать массив объектов с соответствующими суммами из существующего массива в JS - PullRequest
0 голосов
/ 13 марта 2020

Возьмите следующий массив:

["red", "red", "green", "red", "green", "blue"]

Я хочу превратить это в массив объектов, который включает в себя количество вхождений, например:

[
 { name: red, count: 3 },
 { name: green, count: 2 },
 { name: blue, count: 1 },
]

Как мне этого добиться в Javascript? Я использую библиотеку Loda sh, поэтому +1, если есть простое решение, использующее это.

Ответы [ 4 ]

2 голосов
/ 13 марта 2020

То, что вы хотите достичь, это карта частот. Во-первых, вы можете построить карту частот. Используя значение в массиве в качестве ключа, вы можете легко увеличить счетчик на карте.

Начальное значение для любого вновь добавленного ключа: ((freq[val] || 0) + 1). Это просто означает, получить последнее значение или 0 и увеличить на 1.

Получив карту, вы можете преобразовать ее в массив пар имя / количество.

let arr = ["red", "red", "green", "red", "green", "blue"];

let frequencyMap = arr.reduce((freq, val) => {
  return { ...freq, [val]: ((freq[val] || 0) + 1) }
}, {});

let results = Object.keys(frequencyMap).map(key => {
  return { name : key, count : frequencyMap[key] }
});

console.log(results);
.as-console-wrapper { top: 0; max-height: 100% !important; }

Код Golf

Это однострочник с функцией отображения. :)

const freqArray = (a, fn) => ((f) => Object.keys(f).map(k => fn(k, f[k])))(a.reduce((m, v) => ({ ...m, [v]: ((m[v] || 0) + 1) }), {}));

let arr = [ "red", "red", "green", "red", "green", "blue" ];
let freq = freqArray(arr, (k, v) => ({ name : k, count : v }));

console.log(freq);
.as-console-wrapper { top: 0; max-height: 100% !important; }

Сортировка результатов

Вы также можете отсортировать результаты по имени, но если вы это сделаете (так как ключи уникальны) сортировать по количеству не нужно, потому что имена будут уникальными.

const freqArray = (arr, opts) => ((freq) => Object.keys(freq).map(key => opts.mapFn(key, freq[key])))(arr.reduce((m, v) => ({ ...m, [v]: ((m[v] || 0) + 1) }), {})).sort(opts.cmpFn);

let arr = [ "red", "red", "green", "red", "green", "blue" ];
let freq = freqArray(arr, {
  mapFn : (key, val) => ({ name : key, count : val }),
  cmpFn : (a, b) => a.name.localeCompare(b.name) || (a.count - b.count)
});

console.log(freq);
.as-console-wrapper { top: 0; max-height: 100% !important; }

Вы можете отменить условие сортировки для сортировки по количеству (des c), а затем по имени:

const freqArray = (arr, opts) => ((freq) => Object.keys(freq).map(key => opts.mapFn(key, freq[key])))(arr.reduce((m, v) => ({ ...m, [v]: ((m[v] || 0) + 1) }), {})).sort(opts.cmpFn);

let arr = [ "red", "red", "green", "red", "green", "blue", "green", "blue", "blue" ];
let freq = freqArray(arr, {
  mapFn : (key, val) => ({ name : key, count : val }),
  cmpFn : (a, b) => (b.count - a.count) || a.name.localeCompare(b.name)
});

console.log(freq);
.as-console-wrapper { top: 0; max-height: 100% !important; }
1 голос
/ 13 марта 2020

Поскольку вы используете loda sh, вы можете использовать _.countBy() для подсчета количества повторений определенного слова, давая в результате следующий объект:

{
  "red": 3,
  "green": 2,
  "blue": 1
}

Когда у вас есть этот объект, вы можете использовать _.map(), чтобы сопоставить его так, чтобы ключ стал значением свойства name, а значение стало значением свойства count:

const arr = ["red", "red", "green", "red", "green", "blue"];

const getFreq = _.flow(
  _.countBy,
  gr => _.map(gr, (count, name) => ({name, count}))
);

console.log(getFreq(arr));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

Здесь _.flow() используется для создания функции, которая затем может быть использована для группировки дополнительных массивов, если это необходимо, однако, приведенный выше можно упростить, если вы сразу после вывода:

const res = _(arr).countBy().map((count, name) => ({name, count})).value();
0 голосов
/ 13 марта 2020

здесь мы используем countBy () из lodash; удобочитаемый и простой.

const arr = ["red", "red", "green", "red", "green", "blue"];
const result = [];

for ( [key, value] of Object.entries(_.countBy(arr)) ){
  result.push( { 
    name : key,
    count: value
  })
}

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
0 голосов
/ 13 марта 2020

Использовать Array.reduce , начиная с пустого массива, на каждой итерации искать объект с текущим значением как name, если это так, увеличивать счетчик, в противном случае pu sh новый объект :

const arr = ["red", "red", "green", "red", "green", "blue"];

const result = arr.reduce((acc, curr) => {
  const ndx = acc.findIndex(e => e.name === curr);

  if (ndx > -1) {
    acc[ndx].count++;
  } else {
    acc.push({
      name: curr,
      count: 1
    });
  }
  return acc;
}, []);

console.log(result);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...