#javascript #arrays #json #object
#javascript #массивы #json #объект
Вопрос:
Мне не удалось найти решение, идентичное проблеме, которую я пытаюсь решить. Если это дубликат, пожалуйста, предоставьте ссылку на решение.
Используя ванильный Javascript, объедините массив объектов, имеющих общее значение. У меня есть файл JSON, и он создаст следующий объект javascript (я не могу изменить исходную структуру). Обратите внимание, что каждый объект будет иметь разное вложенное имя и вложенное значение. Однако общим значением является name[0]
var data = [
{
name: [
'Data 1', // common value
'1 Jan, 2019', // same value therefore not overwrite/merge
'hotfix dec'
],
value: [
'hotfix1.fsfix',
'hotfix1.txt'
]
},
{
name: [
'Data 1', // common value
'1 Jan, 2019' // same value therefore not overwrite/merge
],
value: 'data1.jar'
},
{
name: [
'Data 2',
'1 Feb, 2019'
],
value: 'data2.fsfix'
},
{
name: [
'Data 2',
'1 Feb, 2019'
],
value: 'data2.jar'
},
{
name: [
'Data 3',
'1 Mar, 2018'
],
value: 'data3.fsfix'
}
]
Результатом desire будет объединение вложенного объекта с тем же именем[0].
var data = [
{
name: [
'Data 1', // common value
'1 Jan, 2019', // same value therefore not overwrite/merge
'hotfix dec'
],
value: [
'data1.fsfix',
'data1.txt',
'data1.jar' // This was added after the merge
]
},
{
name: [
'Data 2',
'1 Feb, 2019'
],
value: [
'data2.fsfix',
'data2.jar' // This was added after the merge
]
},
{
name: [
'Data 3',
'1 Mar, 2018'
],
value: 'data3.fsfix'
}
]
Используя эту новую объединенную структуру, я бы создал функцию для зацикливания каждого набора массивов. Заранее спасибо
Комментарии:
1. вы уже пробовали сами? я бы сделал это с помощью array.reduce
2. Можете ли вы предоставить код, который вы уже сделали?
3. даже если вы можете сделать это с помощью array.reduce, как предлагает Крис Ли, но обычно используется сокращение массива до одного значения. Для вашей проблемы я бы рекомендовал использовать array.map
Ответ №1:
Вы могли бы ввести данные по первому имени, используя карту. Затем введите данные в value
свойство в пределах соответствующего значения Map, также соберите все дополнительные значения в name
массивах (за пределами первых двух записей) и, наконец, извлеките значения Map.
Это работает с линейной временной сложностью:
var data = [{name: ['Data 1', '1 Jan, 2019', 'hotfix dec'],value: ['hotfix1.fsfix','hotfix1.txt']},{name: ['Data 1', '1 Jan, 2019'],value: 'data1.jar'},{name: ['Data 2','1 Feb, 2019'],value: 'data2.fsfix'},{name: ['Data 2','1 Feb, 2019'],value: 'data2.jar'},{name: ['Data 3','1 Mar, 2018'],value: 'data3.fsfix'}];
const map = new Map(data.map(o =>
[o.name[0], { name: o.name.slice(0,2), value: [] }]));
data.forEach(o => map.get(o.name[0]).value.push(...[].concat(o.value)));
data.forEach(o => map.get(o.name[0]).name.push(...o.name.slice(2)));
const result = Array.from(map.values(), o => o.value.length === 1
? { ...o, value: o.value[0] } : o);
console.log(result);
Функция обратного вызова, которая передается Array.from
, является map-функцией. Требуется только преобразовать массивы значений, содержащие только одну строку, только в эту строку. Если это не требуется, вы можете опустить этот обратный вызов и просто сделать Array.from(map.values())
.
Комментарии:
1. Спасибо за подробное объяснение. Результат достаточно близок, но отсутствует имя [2] -> исправление dec только из-за объединения (o.value). Извините за глупый вопрос, но что такое …o и …[]?
2. Я обновил свой ответ, чтобы включить это отсутствующее
name[2]
.o
является параметром функции (это просто имя, которое я выбрал); это переменная, которая соответствует одному из объектов в массиве данных.[]
является пустым массивом.[].concat
расширяет этот пустой массив аргументом, переданнымconcat
, который может быть простым значением или массивом.3. Большое спасибо. Работает идеально. Я имел в виду, что такое термин / функция для 3 точек … перед [] и o? Пытался поискать в Google, но ничего не нашел.
4. Это синтаксис spread. Поскольку
[].concat()
возвращает массив,...
элементы массива будут «распределены» как отдельные аргументы функции. То же самое сo.name.slice(2)
(также массив).5. Прохладный… Я многому учусь у вас! Большое спасибо. С благодарностью
Ответ №2:
Я думаю, что с помощью некоторых методов массива вы можете заставить это работать (непроверенный код ниже, но я думаю, что он должен делать то, что вы хотите):
// Merge matching entries by pushing values from matching objects to each other
data = data.map(entry => {
let matchingObjects = data.filter(match => {
return match.name[0] === entry.name[0];
});
matchingObjects.forEach(match => {
if (match !== entry) {
var flatValue = [entry.value].flat();
entry.value = flatValue.push.apply(flatValue, [match.value].flat());
}
});
});
// Remove duplicates by filtering out all but the first entry of each name[0]
data = data.filter((entry, index) => {
return index === data.findIndex(match => {
return match.name[0] === entry.name[0];
});
});
Комментарии:
1. Спасибо за предложение. Выполнял проверку jsfiddle.net/a0e29xjz но он возвращает неперехваченную ошибку типа: не удается прочитать свойство ‘name’ из undefined (строка 6)
Ответ №3:
Вы также можете использовать reduce
и map
для создания желаемого результата. Я думаю, что в целом это довольно мощная комбинация.
const data = [{name: ['Data 1', '1 Jan, 2019', 'hotfix dec'],value: ['hotfix1.fsfix','hotfix1.txt']},{name: ['Data 1', '1 Jan, 2019'],value: 'data1.jar'},{name: ['Data 2','1 Feb, 2019'],value: 'data2.fsfix'},{name: ['Data 2','1 Feb, 2019'],value: 'data2.jar'},{name: ['Data 3','1 Mar, 2018'],value: 'data3.fsfix'}];
const dataMap = data.reduce((acc, curr)=>{
id = curr.name[0];
acc[id] = acc[id] || {
name: [],
value: []
};
const value = Array.isArray(curr.value) ? curr.value : [curr.value]
acc[id].name = [...acc[id].name, ...curr.name.filter((i)=>acc[id].name.indexOf(i)===-1)]
acc[id].value = [...acc[id].value, ...value.filter((i)=>acc[id].value.indexOf(i)===-1)]
return acc
},{})
const result = Object.keys(dataMap).map(key=> {
const d = dataMap[key];
d.value = d.value.length===1 ? d.value[0] : d.value
return d;
})
Комментарии:
1. Большое спасибо. Работает как шарм!