Я запутался в преобразовании массива с помощью метода уменьшения. Я не могу понять, как работать с вложенным объектом с литеральными ключами.
Чтобы получить некоторое преимущество, я опубликую пример, который я написал, и он отлично работает:
// consider flat array of objects
const names = [
{ name: "Jaden", sname: "Smith", age: 33 },
{ name: "Will", sname: "Smith", age: 12 },
{ name: "Jada", sname: "Smith", age: 29 },
{ name: "Jose", sname: "Varho", age: 21 },
{ name: "Josephina", sname: "Varho", age: 44 },
{ name: "Keanu ", sname: "Reeves", age: 44 }] ;
// suppose i need to transform that array to this shape:
/* {
"Smith": {
"Jaden": 33,
"Will": 12,
"Jada": 29
},
"Varho": {
"Jose": 21,
"Josephina": 44
},
"Reeves": {
"Keanu ": 44
}
}
*/
// this reducer do it's fine:
const shapeIt = (acc, item) => {
console.info('acc:', JSON.stringify(acc));
acc[item.sname] = { ...acc[item.sname], [item.name]: item.age }
return acc
}
const transformShape= (arr) => {
return arr.reduce((acc, item) => shapeIt(acc, item), {});
}
transformShape(names); //gives required shape
Итак, теперь давайте представим, что у меня есть более сложный массив, например:
const flatArray = [
{ "blockId": "first-block", "sectionId": "first-section", "fieldId": "_1", "value": "0" },
{ "blockId": "first-block", "sectionId": "first-section", "fieldId": "_2", "value": "1" },
{ "blockId": "first-block", "sectionId": "second-section", "fieldId": "_1", "value": "1" },
{ "blockId": "second-block", "sectionId": "first-section", "fieldId": "_1", "value": "1" },
{ "blockId": "second-block", "sectionId": "some-section", "fieldId": "_2", "value": "3" },
{ "blockId": "third-block", "sectionId": "other-section", "fieldId": "_1", "value": "3" }];
// and i strictly need to get this shape of object:
/* {
"first-block": {
"first-section": {
"_1": "0",
"_2": "1"
},
"second-section": {
"_1": "1"
}
},
"second-block": {
"first-section": {
"_1": "1"
},
"some-section": {
"_2": "3"
}
},
"third-block": {
"other-section": {
"_1": "3"
}
}
}
*/
В данный момент я пишу такую функцию сокращения. Это работает, но дает мне только последние fieldId
ключи каждого раздела в блоке. если раздел в одном блоке имеет более одного fieldId
- он его теряет. Я слежу за аккумулятором и вижу, что накапливаются только ключи с разными blockId, sectionId, но не разные fieldId
.
const shapeComplex = (acc, item) => {
console.info('acc:', JSON.stringify(acc));
acc[item.blockId] = { ...acc[item.blockId], [item.sectionId]: { [item.fieldId]: item.value } }
return acc
}
const transformComplex = (arr) => {
console.info('initialArr: ', arr)
return arr.reduce((acc, item) => shapeComplex(acc, item), {});
}
transformComplex(flatArray);
// it gives me shape with only last idField in same section and block:
/*
{
"first-block": {
"first-section": {
"_1": "0"
},
"second-section": {
"_1": "1"
}
},
"second-block": {
"first-section": {
"_1": "1"
},
"some-section": {
"_2": "3"
}
},
"third-block": {
"other-section": {
"_1": "3"
}
}
}
*/
Любая помощь, пожалуйста.
Вы можете взять массив нужных ключей для группировки и взять либо значение, либо новый объект для следующего уровня.
const
data = [{ blockId: "first-block", sectionId: "first-section", fieldId: "_1", value: "0" }, { blockId: "first-block", sectionId: "first-section", fieldId: "_2", value: "1" }, { blockId: "first-block", sectionId: "second-section", fieldId: "_1", value: "1" }, { blockId: "second-block", sectionId: "first-section", fieldId: "_1", value: "1" }, { blockId: "second-block", sectionId: "some-section", fieldId: "_2", value: "3" }, { blockId: "third-block", sectionId: "other-section", fieldId: "_1", value: "3" }],
keys = ['blockId', 'sectionId', 'fieldId'],
result = data.reduce((r, o) => {
keys.reduce(
(q, k, i, { length }) => q[o[k]] ??= i + 1 === length ? o.value : {},
r
);
return r;
}, {});
console.info(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Подход, взяв все значения объекта и назначив последний элемент в качестве значения для наиболее вложенного объекта.
Этот подход основан на порядке значений в каждом объекте.
const
data = [{ blockId: "first-block", sectionId: "first-section", fieldId: "_1", value: "0" }, { blockId: "first-block", sectionId: "first-section", fieldId: "_2", value: "1" }, { blockId: "first-block", sectionId: "second-section", fieldId: "_1", value: "1" }, { blockId: "second-block", sectionId: "first-section", fieldId: "_1", value: "1" }, { blockId: "second-block", sectionId: "some-section", fieldId: "_2", value: "3" }, { blockId: "third-block", sectionId: "other-section", fieldId: "_1", value: "3" }],
result = data.reduce((r, o) => {
const
values = Object.values(o),
value = values.pop();
values.reduce(
(q, k, i, { length }) => q[k] ??= i + 1 === length ? value : {},
r
);
return r;
}, {});
console.info(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
а как узнать что группировать?
@northernwind Если он динамический и может измениться в будущем, еще лучше иметь для него значение массива?
Я упростил свой вопрос. Настоящая логика сложнее, и это не мой случай, к сожалению. Но я не говорю, что это неправильно.
Какие книги мне нужно прочитать, чтобы взять это в голову?
Вам также необходимо скопировать свойства (поля) самого внутреннего (раздела) объекта:
const shapeComplex = (acc, item) => {
console.info('acc:', JSON.stringify(acc));
acc[item.blockId] = {
...acc[item.blockId],
[item.sectionId]: {
...acc[item.blockId]?.[item.sectionId],
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[item.fieldId]: item.value
}
};
return acc
}
Однако я бы рекомендовал либо всегда использовать распространение объектов на каждом уровне
const shapeComplex = (acc, item) => {
return {
...acc,
[item.blockId]: {
...acc[item.blockId],
[item.sectionId]: {
...acc[item.blockId]?.[item.sectionId],
[item.fieldId]: item.value
}
}
};
}
или использовать мутации:
const shapeComplex = (acc, {blockId, sectionId, fieldId, value}) => {
const block = acc[blockId] ?? (acc[blockId] = {});
const section = block[sectionId] ?? (block[sectionId] = {});
section[fieldId] = value;
return acc;
}
Как показал здесь ответ @Nina, вы можете обобщить это на сколь угодно глубокие задания.
хм... кажется, работает так же, как в моем случае.... я получаю только последние поля
@northernwind упс, исправлено
все еще получает только последний элемент... Я трачу на это несколько часов, и мне нужен отдых, чтобы проверить его дальше
упс... первый отредактированный образец работает, а второй нет
@northernwind Вот что получается, если исправлять в спешке… Я обновил только первый сниппет, пропустил такое же исправление во втором. Также в третьем была еще одна опечатка. Теперь проверил все три из них и исправил ответ.
Вы можете сделать следующее,
const flatArray = [
{ "blockId": "first-block", "sectionId": "first-section", "fieldId": "_1", "value": "0" },
{ "blockId": "first-block", "sectionId": "first-section", "fieldId": "_2", "value": "1" },
{ "blockId": "first-block", "sectionId": "second-section", "fieldId": "_1", "value": "1" },
{ "blockId": "second-block", "sectionId": "first-section", "fieldId": "_1", "value": "1" },
{ "blockId": "second-block", "sectionId": "some-section", "fieldId": "_2", "value": "3" },
{ "blockId": "third-block", "sectionId": "other-section", "fieldId": "_1", "value": "3" }];
const shapeComplex = (acc, item) => {
if (acc[item.blockId]) {
acc[item.blockId] = { ...acc[item.blockId], [item.sectionId]: {...acc[item.blockId][item.sectionId], [item.fieldId]: item.value } }
} else {
acc[item.blockId] = { ...acc[item.blockId], [item.sectionId]: {[item.fieldId]: item.value } }
}
return acc
}
const transformComplex = (arr) => {
return arr.reduce((acc, item) => shapeComplex(acc, item), {});
}
console.info(transformComplex(flatArray));
На данный момент это только один рабочий код в моем случае. Не знаю кто минусует, не вижу в этом ничего плохого
Вы делаете дерево из плоской структуры данных. Укажите древовидную иерархию (уровни) и свойства, используемые для создания элемента данных конечного узла.
Пример:
function treeFrom (arr, levels, name, value) {
return arr.reduce(
(root,item) =>
{
var node = root;
for (let level of levels) /* descend to bottom tier */
{
levelvalue = item[level];
if (! node[levelvalue]) node[levelvalue] = {};
node = node[levelvalue];
}
node[item[name]] = item[value];
return root;
}
,
{}
)
}
console.info ( treeFrom(flatArray, ['blockId', 'sectionId'], 'fieldId', 'value') );
Это очень интересно, к сожалению, я не могу собрать "ключи" в виде массива, потому что он динамический и может измениться в будущем.