#javascript #algorithm #optimization
#javascript #алгоритм #оптимизация
Вопрос:
У меня есть массив объектов
[
{ hashtag: [ 'yo' ], type: [] },
{ hashtag: [ 'yo2' ], type: [] },
{ hashtag: [ 'yo3' ], type: [] },
{ hashtag: [ 'yo4' ], type: [] },
{ hashtag: [ 'yo4' ], type: [] },
{ hashtag: [ 'yo5' ], type: [ 'email' ] },
{ hashtag: [ 'yo5' ], type: [ 'link' ] },
{ hashtag: [ 'asdasdasd' ], type: [ 'email' ] },
{ hashtag: [ 'yo5' ], type: [ 'link' ] },
{ hashtag: [ 'yo6' ], type: [ 'link' ] },
{ hashtag: [ 'yo6' ], type: [ 'link' ] },
{ hashtag: [ 'yo7' ], type: [ 'link' ] },
{ hashtag: [ 'book', 'hello', 'yo5' ], type: [ 'link' ] }
];
Каждое hashtag
значение может содержать массив строк. Мне нужно получить весь массив уникальных hashtags
объектов, которые имеют точное type
значение, и подсчитать их. Ожидаемый результат:
[
{ hashtag: 'yo5', sum: 3 },
{ hashtag: 'yo6', sum: 2 },
{ hashtag: 'yo7', sum: 1 },
{ hashtag: 'book', sum: 1 },
{ hashtag: 'hello', sum: 1 }
]
Я написал код, но мне кажется, что он недостаточно оптимизирован. Есть ли способ сделать это лучше?
const type = 'link';
const enter = [
{ hashtag: [ 'yo' ], type: [] },
...
{ hashtag: [ 'book', 'hello', 'yo5' ], type: [ 'link' ] }
];
const filteredByType = enter.filter((el) => {
return el.type.includes(type)
});
const newArr = [];
filteredByType.map((el) => {
for (let i = 0; i < el.hashtag.length; i ) {
const z = newArr.filter((e) => {
return e.hashtag === el.hashtag[i]
});
if (z.length) {
const p = newArr.findIndex((obj) => {
return obj.hashtag === el.hashtag[i];
});
newArr[p].sum = 1;
} else {
newArr.push({
hashtag: el.hashtag[i],
sum: 1
})
}
}
});
console.log('newArr', newArr);
Вот рабочий пример
Комментарии:
1. Если только уникальные,
type
значения должны учитываться, неhashtag: 'yo5'
должны иметь соответствующихsum: 2
, поскольку'email'
и'link'
являются только 2 уникальными значениями?2. @evgengorbunkov это должно быть подсчитано для точности
type
, я немного исправил свой вопрос. результат в моем случае правильный3. Тогда что вы подразумеваете под «которые имеют точный тип» ?
4. @Evgengorbunkov в моем коде я передаю его как const
type = 'link'
в первой строке. Итак, мне нужно: из массива, тип которого —link
получить все хэштеги и суммировать количество объектов, содержащих этот хэштег
Ответ №1:
Вы могли бы взять a Map
, посчитать вхождения и создать из него новый массив.
const
getCount = (array, type) => Array.from(
data.reduce(
(map, o) => o.type.includes(type)
? o.hashtag.reduce((m, v) => m.set(v, (m.get(v) || 0) 1), map)
: map,
new Map
),
([hashtag, sum]) => ({ hashtag, sum })
),
data = [{ hashtag: ['yo'], type: [] }, { hashtag: ['yo2'], type: [] }, { hashtag: ['yo3'], type: [] }, { hashtag: ['yo4'], type: [] }, { hashtag: ['yo4'], type: [] }, { hashtag: ['yo5'], type: ['email'] }, { hashtag: ['yo5'], type: ['link'] }, { hashtag: ['asdasdasd'], type: ['email'] }, { hashtag: ['yo5'], type: ['link'] }, { hashtag: ['yo6'], type: ['link'] }, { hashtag: ['yo6'], type: ['link'] }, { hashtag: ['yo7'], type: ['link'] }, { hashtag: ['book', 'hello', 'yo5'], type: ['link'] }];
console.log(getCount(data, 'link'));
console.log(getCount(data, 'email'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Комментарии:
1. Работает не так, как ожидалось. Он должен учитывать тип. Но решение очень полезно
2. @angelzzz, извини, я пропустил эту
type
часть. пожалуйста, смотрите Редактирование.3. Спасибо! Отлично работает
Ответ №2:
Если под суммой вы на самом деле подразумеваете количество, вы можете использовать Map
вместе с Array.prototype.reduce()
для группировки вашего массива по hashtag
значению.
Итак, предполагая, что ваш type
файл не содержит повторяющихся элементов, вы можете получить что-то вроде этого:
const src=[{hashtag:["yo"],type:[]},{hashtag:["yo2"],type:[]},{hashtag:["yo3"],type:[]},{hashtag:["yo4"],type:[]},{hashtag:["yo4"],type:[]},{hashtag:["yo5"],type:["email"]},{hashtag:["yo5"],type:["link"]},{hashtag:["asdasdasd"],type:["email"]},{hashtag:["yo5"],type:["link"]},{hashtag:["yo6"],type:["link"]},{hashtag:["yo6"],type:["link"]},{hashtag:["yo7"],type:["link"]},{hashtag:["book","hello","yo5"],type:["link"]}],
groupSearch = _type =>
[...src
.reduce((acc, {hashtag, type}) => {
const increment = type.includes(_type)
increment amp;amp;
hashtag.forEach(tag => {
const group = acc.get(tag)
group
? group.sum = increment
: acc.set(tag, {hashtag:tag, sum: increment})
})
return acc
}, new Map)
.values()
]
console.log(groupSearch('link'))
.as-console-wrapper{min-height:100%;}
Комментарии:
1. Я не вижу в вашем коде, где вы считаете, что он должен иметь точное
type
значение. Но интересно, как вы использовали Map и Set. Я постараюсь использовать это в своем коде2. @angelzzz: Я понял вашу точку зрения и скорректировал свое решение, чтобы обеспечить именно ожидаемый результат.
3. @angelzzz : рассмотрим небольшой аварийный люк, который я добавил в последней правке моего ответа, что значительно повышает производительность
Ответ №3:
я сделал это, понятия не имею, как это сработало …?
const data =
[ { hashtag: [ 'yo' ], type: [] }
, { hashtag: [ 'yo2' ], type: [] }
, { hashtag: [ 'yo3' ], type: [] }
, { hashtag: [ 'yo4' ], type: [] }
, { hashtag: [ 'yo4' ], type: [] }
, { hashtag: [ 'yo5' ], type: [ 'email' ] }
, { hashtag: [ 'yo5' ], type: [ 'link' ] }
, { hashtag: [ 'asdasdasd' ], type: [ 'email' ] }
, { hashtag: [ 'yo5' ], type: [ 'link' ] }
, { hashtag: [ 'yo6' ], type: [ 'link' ] }
, { hashtag: [ 'yo6' ], type: [ 'link' ] }
, { hashtag: [ 'yo7' ], type: [ 'link' ] }
, { hashtag: [ 'book', 'hello', 'yo5' ], type: [ 'link'] }
]
const getCount = ( arr, typeK ) =>
arr.reduce((res, {hashtag,type }) => [...res,...(type.includes(typeK)?hashtag:[])],[])
.sort()
.reduce((res,val,i,{ [i -1]: last }) =>
{
if (last === val ) res[res.length -1].sum
else res.push({hastag:val,sum:1})
return res
},[])
console.log('link =', getCount(data, 'link'))
console.log('email =', getCount(data, 'email'))
.as-console-wrapper { max-height: 100% !important; top: 0; }
Комментарии:
1. Что касается производительности, я бы ожидал, что она может быть относительно медленной из-за
.sort()
(что само по себе довольно дорого). Однако я могу ошибаться, не могу сравнить это с другими, потому что это не дает ожидаемого результата.2. @Evgengorbunkov действительно, я полностью упустил этот момент. Я поправил 😉
3. @Evgengorbunkov ожидаемый результат представлен в исходном вопросе и в jsfiddle
4. @angelzzz: Я имел в виду, что этот конкретный ответ не предоставил результат, который вы указали, как ожидалось, поэтому его нельзя сравнивать Apple с Apple с другими решениями, поскольку они выполняют желаемый результат.
5. Однако одно предположение, которое я сделал правильно : приведенное выше решение является одним из самых медленных, в то время как самыми быстрыми являются те, которые опубликовала Нина, и те, которые опубликовал я — в разных тестовых тестах ее или мои тесты показывают самую высокую производительность.
Ответ №4:
Другой способ…
const data =
[ { hashtag: [ 'yo' ], type: [] }
, { hashtag: [ 'yo2' ], type: [] }
, { hashtag: [ 'yo3' ], type: [] }
, { hashtag: [ 'yo4' ], type: [] }
, { hashtag: [ 'yo4' ], type: [] }
, { hashtag: [ 'yo5' ], type: [ 'email' ] }
, { hashtag: [ 'yo5' ], type: [ 'link' ] }
, { hashtag: [ 'asdasdasd' ], type: [ 'email' ] }
, { hashtag: [ 'yo5' ], type: [ 'link' ] }
, { hashtag: [ 'yo6' ], type: [ 'link' ] }
, { hashtag: [ 'yo6' ], type: [ 'link' ] }
, { hashtag: [ 'yo7' ], type: [ 'link' ] }
, { hashtag: [ 'book', 'hello', 'yo5' ], type: [ 'link'] }
]
const getCount = ( arr, typeK ) =>
Object.entries(arr.reduce((r,{hashtag,type})=>
{
if (type.includes(typeK))
hashtag.forEach(h=>{ r[h]=(r[h] ?? 0); r[h] })
return r
},{})).map(([k,v])=> ({hashtag:k,sum:v}))
console.log('link =', getCount(data, 'link'))
console.log('email =', getCount(data, 'email'))
.as-console-wrapper { max-height: 100% !important; top: 0; }
Комментарии:
1. И это тоже.
2. @Evgengorbunkov действительно, я полностью пропустил этот момент и здесь. Я исправил 😉
Ответ №5:
Другое решение с использованием двойного reduce
. Мы отфильтровываем те значения, которые не включают наш целевой тип, складываем результат в объект like {yo5: 3, yo6: 2, yo7: 1, book: 1, hello: 1}
, берем записи этого и сопоставляем их с вашим {hashtag, sum}
форматом:
const tagCountByType = (targetType) => (xs) =>
Object .entries (xs .filter (({type}) => type .includes (targetType))
.reduce (
(a, {hashtag}) => hashtag .reduce ((a, tag) => ((a [tag] = (a[tag] || 0) 1), a), a),
{}
)
) .map (([hashtag, sum]) => ({hashtag, sum}))
const input = [{hashtag: ['yo'], type: []}, {hashtag: ['yo2'], type: []}, {hashtag: ['yo3'], type: []}, {hashtag: ['yo4'], type: []}, {hashtag: ['yo4'], type: []}, {hashtag: ['yo5'], type: ['email']}, {hashtag: ['yo5'], type: ['link']}, {hashtag: ['asdasdasd'], type: ['email']}, {hashtag: ['yo5'], type: ['link']}, {hashtag: ['yo6'], type: ['link']}, {hashtag: ['yo6'], type: ['link']}, {hashtag: ['yo7'], type: ['link']}, {hashtag: ['book', 'hello', 'yo5'], type: ['link']}]
console .log ('link:', tagCountByType ('link') (input))
console .log ('email:', tagCountByType ('email') (input))
.as-console-wrapper {max-height: 100% !important; top: 0}
Как указывали другие, sum
здесь странный выбор слов. count
имело бы больше смысла.