#javascript #arrays #ecmascript-6 #lodash
#javascript #массивы #ecmascript-6 #Lodash
Вопрос:
У меня довольно сложная проблема, которую я, похоже, не могу понять. У меня есть два массива объектов, для которых я хотел бы объединить оценки. Он должен объединять / добавлять определенные свойства на основе оценок. Например, между двумя массивами всего 4 gameId
, причем 3 из них уникальны. При слиянии он должен объединять _scores
раздел, если он одинаковый gameId
, поэтому в этом случае это будет как EarthNormal
слияние. Но проблема в том, что иногда у score
in _scores
могут быть повторяющиеся оценки, поэтому BAR и BASH выглядят почти одинаково, но отличаются друг от друга, их можно добавить, но FOO
оценка одинакова для обоих, поэтому я не хочу, чтобы она объединялась в оценки (если это имеет смысл).
const arr1 = [{
"gameId": "AirNormal",
"_scores":
[{
"score": 144701,
"playerName": "FOO",
"fullCombo": true,
"timestamp": 1599968866
}]
}, {
"gameId": "EarthNormal",
"_scores":
[{
"score": 177352,
"playerName": "BAR",
"fullCombo": true,
"timestamp": 1599969253
}, {
"score": 164665,
"playerName": "FOO",
"fullCombo": false,
"timestamp": 1599970971
}]
}];
const arr2 = [{
"gameId": "EarthNormal",
"_scores":
[{
"score": 177352,
"playerName": "BASH",
"fullCombo": false,
"timestamp": 1512969017
}, {
"score": 164665,
"playerName": "FOO",
"fullCombo": false,
"timestamp": 1599970971
}]
}, {
"gameId": "FireNormal",
"_scores":
[{
"_score": 124701,
"_playerName": "FOO",
"_fullCombo": true,
"_timestamp": 1591954866
}]
}];
Я бы хотел, чтобы окончательный объединенный массив выглядел так:
mergedArray = [{
"gameId": "AirNormal",
"_scores":
[{
"score": 144701,
"playerName": "FOO",
"fullCombo": true,
"timestamp": 1599968866
}]
}, {
"gameId": "EarthNormal",
"_scores":
[{
"score": 177352,
"playerName": "BAR",
"fullCombo": true,
"timestamp": 1599969253
}, {
"score": 177352,
"playerName": "BASH",
"fullCombo": false,
"timestamp": 1512969017
}, {
"score": 164665,
"playerName": "FOO",
"fullCombo": false,
"timestamp": 1599970971
}]
}, {
"gameId": "FireNormal",
"_scores":
[{
"score": 124701,
"playerName": "FOO",
"fullCombo": true,
"timestamp": 1591954866
}]
}]
Я пытался сделать это и использовать lodash
:
let merged = [...arr1, ...arr2];
merged = _.uniqBy[merged, 'gameId']
let scoresMerge = _.uniqBy[merged, '_scores']
console.log(scoresMerge);
но это сработало не так, как я ожидал. Я неправильно подхожу к этому?
Ответ №1:
Это довольно просто с использованием ванильного javascript.
- объединение массивов с помощью деструктурирования
reduce()
объединенные массивы в объект, индексируемыйgameId
- проверьте все свойства каждого
_score
объекта по сравнению с накопленным_scores
массивом с помощью.some()
и нажмите, если совпадение не найдено. - возвращает значения уменьшенного объекта с помощью
Object.values()
const arr1 = [{ "gameId": "AirNormal", "_scores": [{ "score": 144701, "playerName": "FOO", "fullCombo": true, "timestamp": 1599968866 }]}, { "gameId": "EarthNormal", "_scores": [{ "score": 177352, "playerName": "BAR", "fullCombo": true, "timestamp": 1599969253 }, { "score": 164665, "playerName": "FOO", "fullCombo": false, "timestamp": 1599970971 }]}];
const arr2 = [{"gameId": "EarthNormal","_scores":[{"score": 177352,"playerName": "BASH","fullCombo": false,"timestamp": 1512969017}, {"score": 164665,"playerName": "FOO","fullCombo": false,"timestamp": 1599970971}]}, {"gameId": "FireNormal","_scores":[{"_score": 124701,"_playerName": "FOO","_fullCombo": true,"_timestamp": 1591954866}]}];
const merged = Object.values([...arr1, ...arr2].reduce((a, {gameId, _scores}) => {
// retrieve gameId object otherwise initialize it.
a[gameId] = {...a[gameId] ?? {gameId, _scores: []}};
// iterate over all _score objects
_scores.forEach(s => {
// if accumulator _scores array doesn't have an object matching all properties, push _score
if (!a[gameId]['_scores'].some(o => {
return !Object.entries(s).some(([k, v]) => o[k] !== v)})
) {
a[gameId]['_scores'].push({...s});
}
});
return a;
}, {}));
console.log(merged);
Ответ №2:
Вам нужно идентифицировать объекты с одинаковыми gameId
, а затем объединить и дедуплицировать их _.scores
массив.
Легко объединить / дедуплицировать элементы массива, не являющиеся примитивными, используя Array.reduce()
и карту. Для каждого элемента вы проверяете, есть ли запрошенный ключ уже на карте. Если это не так, вы присваиваете текущий элемент ключу карты. Если это вы, замените / объедините текущий элемент с элементом на карте.
После завершения итерации карты используйте Array.from()
для преобразования .values()
итератора карты в массив.
const arr1 = [{"gameId":"AirNormal","_scores":[{"score":144701,"playerName":"FOO","fullCombo":true,"timestamp":1599968866}]},{"gameId":"EarthNormal","_scores":[{"score":177352,"playerName":"BAR","fullCombo":true,"timestamp":1599969253},{"score":164665,"playerName":"FOO","fullCombo":false,"timestamp":1599970971}]}];
const arr2 = [{"gameId":"EarthNormal","_scores":[{"score":177352,"playerName":"BASH","fullCombo":false,"timestamp":1512969017},{"score":164665,"playerName":"FOO","fullCombo":false,"timestamp":1599970971}]},{"gameId":"FireNormal","_scores":[{"score":124701,"playerName":"FOO","fullCombo":true,"timestamp":1591954866}]}];
const dedupLastBy = (a1 = [], a2 = [], key) => Array.from(
[...a1, ...a2].reduce((acc, obj) => {
const keyName = obj[key];
if(acc.has(keyName)) acc.delete(keyName);
return acc.set(keyName, obj);
}, new Map()).values()
)
const handleDups = ({ _scores: a, ...o1 }, { _scores: b, ...o2 }) => ({
...o1,
...o2,
_scores: dedupLastBy(a, b, 'playerName')
});
const result = Array.from([...arr1, ...arr2]
.reduce((acc, o) => {
const { gameId } = o;
if(acc.has(gameId)) acc.set(gameId, handleDups(acc.get(gameId), o));
else acc.set(gameId, o);
return acc;
}, new Map()).values());
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" integrity="sha512-90vH1Z83AJY9DmlWa8WkjkV79yfS2n2Oxhsi2dZbIv0nC4E6m5AbH8Nh156kkM7JePmqD6tcZsfad1ueoaovww==" crossorigin="anonymous"></script>