Группировка вложенных массивов с lodash

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

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

Вход:

[
  {
    "id": "REPORT1",
    "application": "APP1",
    "type": "TYPE1",
    "title": ""
  },
  {
    "id": "REPORT2",
    "application": "APP1",
    "type": "TYPE1",
    "title": ""
  },
  {
    "id": "REPORT3",
    "application": "APP1",
    "type": "TYPE2",
    "title": ""
  },
  {
    "id": "REPORT4",
    "application": "APP2",
    "type": "TYPE3",
    "title": ""
  }
]

Желаемый результат:

{
  "APP1": {
    "TYPE1": [
      {
        "id": "REPORT1",
        "application": "APP1",
        "type": "TYPE1",
        "title": ""
      },
      {
        "id": "REPORT2",
        "application": "APP1",
        "type": "TYPE1",
        "title": ""
      }
    ],
    "TYPE2": [
      {
        "id": "REPORT3",
        "application": "APP1",
        "type": "TYPE2",
        "title": ""
      }
    ]
  },
  "APP2": {
    "TYPE3": [
      {
        "id": "REPORT4",
        "application": "APP2",
        "type": "TYPE3",
        "title": ""
      }
    ]
  }
}

С loadash я придумал следующее:

const output = _(input)
  .groupBy(report => report.application)
  .value()

После группы по application мне нужно сделать еще одну вложенную группировку или сопоставление по type, но застрял.

Поведение ключевого слова "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) для оценки ваших знаний,...
6
0
6 367
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Это может быть легко реализовано без lodash через функция Array#reduce().

Подробнее о том, как это достигается, см. Комментарии в исходном коде фрагментов кода:

var input = [{
    "id": "REPORT1",
    "application": "APP1",
    "type": "TYPE1",
    "title": ""
  },
  {
    "id": "REPORT2",
    "application": "APP1",
    "type": "TYPE1",
    "title": ""
  },
  {
    "id": "REPORT3",
    "application": "APP1",
    "type": "TYPE2",
    "title": ""
  },
  {
    "id": "REPORT4",
    "application": "APP2",
    "type": "TYPE3",
    "title": ""
  }
];

var output = input.reduce((result, item) => {

  // Get app object corresponding to current item from result (or insert if not present)
  var app = result[item.application] = result[item.application] || {};

  // Get type array corresponding to current item from app object (or insert if not present)
  var type = app[item.type] = app[item.type] || [];

  // Add current item to current type array
  type.push(item);

  // Return the result object for this iteration
  return result;

}, {});

console.info(output);
.as-console-wrapper {
height:100% !important;
max-height:unset !important;
}

Хорошая работа без нагрузки :)

Alexandre Elshobokshy 26.12.2018 10:32

Спасибо за упоминание о сокращении ……

Hossam Maher 29.10.2021 06:34

Добро пожаловать, @HossamMaher

Dacre Denny 29.10.2021 06:41

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

groups = ["application", "type"]

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

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

var data = [{ id: "REPORT1", application: "APP1", type: "TYPE1", title: "" }, { id: "REPORT2", application: "APP1", type: "TYPE1", title: "" }, { id: "REPORT3", application: "APP1", type: "TYPE2", title: "" }, { id: "REPORT4", application: "APP2", type: "TYPE3", title: "" }],
    groups = ["application", "type"],
    grouped = data.reduce((r, o) => {
        groups
            .reduce((group, key, i, { length }) =>
                group[o[key]] = group[o[key]] || (i + 1 === length ? [] : {}), r)
            .push(o);

        return r;
    }, {});
    
console.info(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

es6, распространение и уменьшение объектов делают его проще

const input = [
  {
    "id": "REPORT1",
    "application": "APP1",
    "type": "TYPE1",
    "title": ""
  },
  {
    "id": "REPORT2",
    "application": "APP1",
    "type": "TYPE1",
    "title": ""
  },
  {
    "id": "REPORT3",
    "application": "APP1",
    "type": "TYPE2",
    "title": ""
  },
  {
    "id": "REPORT4",
    "application": "APP2",
    "type": "TYPE3",
    "title": ""
  }
]


const output = input.reduce((acc, item) => ({
  ...acc,
  [item.application]: {
    ...acc[item.application],
    [item.type]: [
      ...(acc[item.application] && acc[item.application][item.type] || []),
      item,
    ]
  }
}), {})

console.info(output)

Я лично нашел это более читаемым при объединении _.groupBy и _.mapValues ​​в Lodash:

var input = [{ id: "REPORT1", application: "APP1", type: "TYPE1", title: "" }, { id: "REPORT2", application: "APP1", type: "TYPE1", title: "" }, { id: "REPORT3", application: "APP1", type: "TYPE2", title: "" }, { id: "REPORT4", application: "APP2", type: "TYPE3", title: "" }]

const output = _.mapValues(_.groupBy(input, i => i.application),app => _.groupBy(app, i => i.type))

console.info(output)
<script src = "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

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