У меня есть массив с несколькими строками, например этот:
["e", "e", "e", "e", "a", "e", "e", "a", "e", "e", "e", "e", "e", "o", "a", "e", "r", "e", "e", "e", "o", "e", "u"]
Теперь я хочу уменьшить этот массив, чтобы он содержал только уникальные строки. В то же время я хочу вести подсчет количества дубликатов и (предпочтительно) вычислять процент, который показывает количество дубликатов, деленное на общее количество строк.
Я думаю, что конечный результат выглядит примерно так:
[
{
letter: "e",
duplicates: 16,
percentage: 0.695652173913043
},
{
letter: "a",
duplicates: 3,
percentage: 0.130434782608696
},
{
letter: "o",
duplicates: 2,
percentage: 0.08695652173913
},
{
letter: "r"
duplicates: 1,
percentage: 0.043478260869565
},
{
letter: "u",
duplicates: 1,
percentage: 0.043478260869565
}
]
Как мне сделать это преобразование в JavaScript/ES6?
Это не школьное задание (мне скоро исполнится 50). Это часть лингвистического эксперимента, над которым я работаю. Кроме того, это забавная задача, и я подумал, что сообщество StackOverflow оценит ее. Я знал, что у меня есть и что я хочу получить, мне просто нужна была помощь, чтобы добраться туда.
Это возможно с помощью Array#from , Array#reduce , Map , Array#map и деструктурирования.
Уменьшение позволяет перегруппировать и подсчитать буквы и их появление. После того, как они были организованы должным образом, вы можете перейти к отображению всех результатов и вычислить процентное соотношение.
const data = ["e", "e", "e", "e", "a", "e", "e", "a", "e", "e", "e", "e", "e", "o", "a", "e", "r", "e", "e", "e", "o", "e", "u"];
const res = Array
.from(data.reduce((acc, cur) => acc.set(cur, (acc.get(cur)||0) + 1), new Map()))
.map(([letter, duplicates]) => ({letter, duplicates, percentage: duplicates / data.length}));
console.info(res);
Большое спасибо! Там довольно много всего происходит, поэтому мне понадобится некоторое время, чтобы прочитать это, чтобы понять все, что происходит, но это работает как шарм!
@tkahn np, если вам нужна более подробная версия, дайте мне знать. Это сжато до максимума.
Array.from
принимает аргумент отображения, т. е. Array.from(data.reduce(...), ([ letter, duplicates ]) => ...)
сделает то же самое за половину итераций.
@Спасибо за совет, однако я не верю, что это сокращает количество итераций вдвое. «Более ясно, Array.from(obj, mapFn, thisArg) имеет тот же результат, что и Array.from(obj).map(mapFn, thisArg), за исключением того, что он не создает промежуточный массив».
Это сокращает количество итераций вдвое, поскольку Array.from
выполняет первый проход по записям карты, создает промежуточный массив, затем Array#map
выполняет второй проход по промежуточному массиву, создавая второй массив. Array.from(myMap, mappingFn)
делает все это за один проход.
Да, применение карты после Array.from создает промежуточный массив, но не означает ли это, что это удваивает количество итераций? Логически параметр Array.from func должен применяться только после того, как Map будет правильно преобразован в массив внутри.
let array = ["e", "e", "e", "e", "a", "e", "e", "a", "e", "e", "e", "e", "e", "o", "a", "e", "r", "e", "e", "e", "o", "e", "u"]
let result = [...new Set(array)].map(e =>({letter:e,
duplicates:array.filter(n => n===e).length,
percentage:array.filter(n => n===e).length/array.length}))
console.info(result)
Спасибо! Это тоже очень хорошее решение моей проблемы! Я вижу, вы использовали синтаксис распространения вместе с Set, с которым я экспериментировал, хотя я так далеко и не продвинулся.
@tkahn на самом деле это расточительное использование спреда, потому что он создает промежуточный массив только для того, чтобы вызвать на нем .map
, что создает другой массив. Array.from(new Set(array), e => ...)
правильно писать [...new Set(array)].map(e => ...)
И что вы пробовали для этого, похожего на школьное задание, требующее ответа?