Уменьшите массив объектов с помощью глубоко литеральных ключей в JS

Я запутался в преобразовании массива с помощью метода уменьшения. Я не могу понять, как работать с вложенным объектом с литеральными ключами.

Чтобы получить некоторое преимущество, я опубликую пример, который я написал, и он отлично работает:

// 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"
        }
    }
}
*/

Любая помощь, пожалуйста.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
138
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы можете взять массив нужных ключей для группировки и взять либо значение, либо новый объект для следующего уровня.

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 13.12.2020 16:42

а как узнать что группировать?

Nina Scholz 13.12.2020 16:46

@northernwind Если он динамический и может измениться в будущем, еще лучше иметь для него значение массива?

Bergi 13.12.2020 16:53

Я упростил свой вопрос. Настоящая логика сложнее, и это не мой случай, к сожалению. Но я не говорю, что это неправильно.

northernwind 13.12.2020 16:55

Какие книги мне нужно прочитать, чтобы взять это в голову?

northernwind 13.12.2020 17:14
Ответ принят как подходящий

Вам также необходимо скопировать свойства (поля) самого внутреннего (раздела) объекта:

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 13.12.2020 16:40

@northernwind упс, исправлено

Bergi 13.12.2020 16:53

все еще получает только последний элемент... Я трачу на это несколько часов, и мне нужен отдых, чтобы проверить его дальше

northernwind 13.12.2020 17:03

упс... первый отредактированный образец работает, а второй нет

northernwind 13.12.2020 17:05

@northernwind Вот что получается, если исправлять в спешке… Я обновил только первый сниппет, пропустил такое же исправление во втором. Также в третьем была еще одна опечатка. Теперь проверил все три из них и исправил ответ.

Bergi 13.12.2020 18:13

Вы можете сделать следующее,

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));

На данный момент это только один рабочий код в моем случае. Не знаю кто минусует, не вижу в этом ничего плохого

northernwind 13.12.2020 16:48

Вы делаете дерево из плоской структуры данных. Укажите древовидную иерархию (уровни) и свойства, используемые для создания элемента данных конечного узла.

Пример:

    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') );

Другие вопросы по теме