У меня есть объект JSON следующим образом:
[{
"name" : "cat",
"value" : 17,
"group" : "animal",
},
{
"name" : "dog",
"value" : 6,
"group" : "animal",
},
{
"name" : "snak",
"value" : 2,
"group" : "animal",
},
{
"name" : "tesla",
"value" : 11,
"group" : "car",
},
{
"name" : "bmw",
"value" : 23,
"group" : "car",
}]
Я хочу преобразовать этот JSON в формат ниже с помощью JS:
[{
"name":"animal",
"children":[
{"name":"cat", "value":17},
{"name":"dog", "value":6},
{"name":"snak", "value":2}
]},
{
"name":"car",
"children":[
{"name":"bmw", "value":11},
{"name":"tesla", "value":23}
]}]
Я пытаюсь преобразовать и отфильтровать с помощью функции Reduce, но мне не удалось преобразовать в этот формат.
Обновлено:
Код, который я тестировал, был таким.
let groupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
let groubedByExchange=groupBy(JSON_data, 'group');



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


const arr = [{
"name": "cat",
"value": 17,
"group": "animal",
},
{
"name": "dog",
"value": 6,
"group": "animal",
},
{
"name": "snak",
"value": 2,
"group": "animal",
},
{
"name": "tesla",
"value": 11,
"group": "car",
},
{
"name": "bmw",
"value": 23,
"group": "car",
}
]
const newFormat = arr.reduce((pre, cur) => {
const group = pre.find(grp => grp.name === cur.group)
if (group) {
group.children.push({
name: cur.name,
value: cur.value
})
return pre
}
const newGroup = {
name: cur.group,
children: [{
name: cur.name,
value: cur.value
}]
}
pre.push(newGroup)
return pre
}, [])
console.info(newFormat)ну вот. сначала вы пытаетесь найти эту группу в новом массиве, если она существует, вы добавляете к ней этого ребенка. если нет, вы создаете группу и дочерний массив и вставляете его в массив
Вы можете построить массив и искать ту же группу в массиве.
var array = [{ name: "cat", value: 17, group: "animal" }, { name: "dog", value: 6, group: "animal" }, { name: "snak", value: 2, group: "animal" }, { name: "tesla", value: 11, group: "car" }, { name: "bmw", value: 23, group: "car" }],
result = array.reduce((r, { group: name, ...object }) => {
var temp = r.find(o => o.name === name);
if (!temp) r.push(temp = { name, children: [] });
temp.children.push(object);
return r;
}, []);
console.info(result);.as-console-wrapper { max-height: 100% !important; top: 0; }Это было бы моим решением ES6, использующим карту и уменьшение:
const data = [
{
name: "cat",
value: 17,
group: "animal"
},
{
name: "dog",
value: 6,
group: "animal"
},
{
name: "snak",
value: 2,
group: "animal"
},
{
name: "tesla",
value: 11,
group: "car"
},
{
name: "bmw",
value: 23,
group: "car"
}
];
const grouped = data.reduce((acc, currItem) => {
const groupKey = currItem.group;
if (!acc[groupKey]) {
acc[groupKey] = [currItem];
} else {
acc[groupKey].push(currItem);
}
return acc;
}, {});
const res = Object.keys(grouped).map(key => ({
name: key,
children: grouped[key].map(groupItem => ({
name: groupItem.name,
value: groupItem.value
}))
}));
console.info(res);Проверьте вывод консоли, чтобы увидеть промежуточные результаты.
Я думаю, что в некоторых других ответах используется ненужный find() (который равен O (n)), в то время как вы можете просто проверить, есть ли уже текущий групповой ключ в O (1).
Я сохраняю сгруппированные результаты в промежуточной переменной grouped для ясности, но все это можно встроить.
Использование Array#from, Array#reduce, Array#concat, деструктуризация, синтаксис распространения и карта.
const data=[{"name":"cat","value":17,"group":"animal",},{"name":"dog","value":6,"group":"animal",},{"name":"snak","value":2,"group":"animal",},{"name":"tesla","value":11,"group":"car",},{"name":"bmw","value":23,"group":"car",}];
const res = Array.from(
data.reduce((a,{group, ...rest})=>{
return a.set(group, [rest].concat(a.get(group)||[]));
}, new Map())
).map(([group, children])=>({group,children}));
console.info(res);Ну вот. Вы можете использовать lodash для достижения того же.
var data = [{
"name": "cat",
"value": 17,
"group": "animal",
},
{
"name": "dog",
"value": 6,
"group": "animal",
},
{
"name": "snak",
"value": 2,
"group": "animal",
},
{
"name": "tesla",
"value": 11,
"group": "car",
},
{
"name": "bmw",
"value": 23,
"group": "car",
}
]
var result = _(data)
.groupBy('group')
.map((group, name) =>
({
name,
children: _.map(group, ({
name: name,
value
}) => ({
name,
value
}))
}))
.value()
console.info(result)<script src = "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>Простое решение — создать промежуточный словарь, а затем преобразовать его в структуру вывода.
Вы можете использовать Array.reduce(), Object.entries() и Array.map(), чтобы сделать это:
const data = [
{ "name" : "cat", "value" : 17, "group" : "animal" },
{ "name" : "dog", "value" : 6, "group" : "animal" },
{ "name" : "snak", "value" : 2, "group" : "animal" },
{ "name" : "tesla", "value" : 11, "group" : "car" },
{ "name" : "bmw", "value" : 23, "group" : "car" }
];
const result = Object.entries(data.reduce((acc, { name, value, group }) => {
acc[group] = (acc[group] || []);
acc[group].push({ name, value });
return acc;
}, {})).map(([key, value]) => ({ name: key, children: value }));
console.info(result);Использование оператора распространения делает его на одну строку короче и по-прежнему читабельным:
const data = [
{ "name" : "cat", "value" : 17, "group" : "animal" },
{ "name" : "dog", "value" : 6, "group" : "animal" },
{ "name" : "snak", "value" : 2, "group" : "animal" },
{ "name" : "tesla", "value" : 11, "group" : "car" },
{ "name" : "bmw", "value" : 23, "group" : "car" }
];
const result = Object.entries(data.reduce((acc, { name, value, group }) => {
acc[group] = [...(acc[group] || []), { name, value }];
return acc;
}, {})).map(([key, value]) => ({ name: key, children: value }));
console.info(result);И на один лайнер короче с оператором запятой:
const data = [
{ "name" : "cat", "value" : 17, "group" : "animal" },
{ "name" : "dog", "value" : 6, "group" : "animal" },
{ "name" : "snak", "value" : 2, "group" : "animal" },
{ "name" : "tesla", "value" : 11, "group" : "car" },
{ "name" : "bmw", "value" : 23, "group" : "car" }
];
const result = Object.entries(data.reduce((acc, { name, value, group }) =>
(acc[group] = [...(acc[group] || []), { name, value }], acc)
, {})).map(([key, value]) => ({ name: key, children: value }));
console.info(result);То же самое можно сделать с Object.assign():
const data = [
{ "name" : "cat", "value" : 17, "group" : "animal" },
{ "name" : "dog", "value" : 6, "group" : "animal" },
{ "name" : "snak", "value" : 2, "group" : "animal" },
{ "name" : "tesla", "value" : 11, "group" : "car" },
{ "name" : "bmw", "value" : 23, "group" : "car" }
];
const result = Object.entries(data.reduce((acc, { name, value, group }) =>
Object.assign(acc, { [group]: [...(acc[group] || []), { name, value }] })
, {})).map(([key, value]) => ({ name: key, children: value }));
console.info(result);И, наконец, немного длиннее, но с многоразовой функцией groupBy:
const data = [
{ "name" : "cat", "value" : 17, "group" : "animal" },
{ "name" : "dog", "value" : 6, "group" : "animal" },
{ "name" : "snak", "value" : 2, "group" : "animal" },
{ "name" : "tesla", "value" : 11, "group" : "car" },
{ "name" : "bmw", "value" : 23, "group" : "car" }
];
const groupBy = prop => data => {
return data.reduce((dict, item) => {
const { [prop]: _, ...rest } = item;
dict[item[prop]] = [...(dict[item[prop]] || []), rest];
return dict;
}, {});
};
const result = Object.entries(groupBy('group')(data))
.map(([key, value]) => ({ name: key, children: value }));
console.info(result);Хорошее использование Object.entries. Мне также нравится acc[group] = (acc[group] || []);, чтобы избежать оператора if. Это должен быть принятый ответ imo.
const transform = things =>
things.reduce((acc, { name, value, group }) => {
const existingGroup = acc.find(g => g.name === group) || {};
return [
...acc.filter(g => g.name !== group),
{
...existingGroup,
name: group,
children: [...(existingGroup.children || []), { name, value }],
},
];
}, []);
Возможный дубликат Групповой массив объектов, в который вложены некоторые ключи с определенными именами