Объединить вложенный массив объектов по общему ключу (без lodash)

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

{
   "category":"e-liquid",
   "subcategories":[
      {
         "attributes":{
            "name":"50ml",
            "id":19
         }
      },
      {
         "attributes":{
            "name":"100ml",
            "id":18
         }
      },
   ],
}

{
   "category":"e-liquid",
   "subcategories":[
      {
         "attributes":{
            "name":"50ml",
            "id":19
         }
      },
      {
         "attributes":{
            "name":"100ml",
            "id":18
         }
      },
   ],
}

{
   "category":"e-liquid",
   "subcategories":[
      {
         "attributes":{
            "name":"50ml",
            "id":19
         }
      }
   ]
}

{
   "category":"hardware",
   "subcategories":[
      {
         "attributes":{
            "name":"tanks",
            "id":15
         }
      }
   ]
}

{
   "category":"hardware",
   "subcategories":[
      {
         "attributes":{
            "name":"tanks",
            "id":15
         }
      },
      {
         "attributes":{
            "name":"coils",
            "id":14
         }
      }
   ]
}

Каждый вышеприведенный объект JSON представляет отдельный продукт.

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

{
   "category":"e-liquid",
   "subcategories":[
      "50ml",
      "100ml",
      "150ml",
      "200ml",
      "...anything else"
   ]
}

{
   "category": "hardware",
   "subcategories":[
      "coils",
      "tanks",
      "batteries",
      "...whatever else"
   ]
}

Любое понимание ценится. Некоторое время искал stackoverflow, но, похоже, ничего не обнаружилось - уже пробовал несколько решений из вопросов с аналогичной формулировкой, но часто слияния были слишком мелкими, и я не могу понять, как я буду глубоко сливать (при условии, что это то, что нужно здесь). Мой lodash не работает в моей конфигурации nuxt, поэтому я специально прошу решения, отличные от lodash, если это возможно.

На вашем входе первый объект имеет атрибуты как объект. В последнем атрибуты представляют собой строку. Это было намеренно?

Andrew Parks 21.11.2022 23:06

@AndrewParks спасибо, что указали на это, сейчас я исправил, это было непреднамеренно, атрибуты - это объект по всем направлениям.

logikurl 22.11.2022 13:51
Поведение ключевого слова "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
2
88
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Получите уникальные имена категорий, затем получите уникальные имена атрибутов для каждой из этих категорий:

const data = [{"category":"e-liquid","subcategories":[{"attributes":{"name":"50ml","id":19}},{"attributes":{"name":"100ml","id":18}}]},{"category":"e-liquid","subcategories":[{"attributes":{"name":"50ml","id":19}},{"attributes":{"name":"100ml","id":18}}]},{"category":"e-liquid","subcategories":[{"attributes":{"name":"50ml","id":19}}]},{"category":"hardware","subcategories":[{"attributes":"tanks","id":15}]},{"category":"hardware","subcategories":[{"attributes":"tanks","id":15},{"attributes":"coils","id":14}]}];

const r = [...new Set(data.map(i=>i.category))].map(i=>({
  category:i,
  subcategories:[...new Set(data.filter(({category:c})=>c===i)
    .flatMap(({subcategories:s})=>s.map(({attributes:a})=>a.name??a)))]
}));

console.info(r);

Это решение кажется кратким, слава моему мужчине, сейчас я просто попытаюсь реализовать его - мне нужно попрактиковаться в том, чтобы разобраться с картами и фильтрами. Судя по всему, это примет любые категории и подкатегории, которые будут брошены на него, и отсортирует правильно? то есть, если другая категория появилась вместе со своими собственными подкатегориями. Однако, как правило, продукт будет иметь только одну категорию и множество подкатегорий. Я подкреплю это на своей стороне.

logikurl 22.11.2022 13:55

@logikurl да, он должен вмещать столько категорий, сколько вам нужно. Кстати, если атрибуты всегда являются объектом, вы можете изменить =>a.name??a просто на =>a.name

Andrew Parks 22.11.2022 16:28

Работал как шарм, короткий, резкий и достаточно читаемый. Пометка как ответ Большое спасибо, Андрей. Это очень помогло мне. Я переименовал вa.name, так как атрибуты да всегда будут объектом, но очень острым глазом и отличным вкладом.

logikurl 22.11.2022 19:24

быстрый вопрос - есть ли способ еще больше сгладить массив подкатегорий, поэтому вместо подкатегорий: [ [ ] ] это подкатегории: []

logikurl 24.11.2022 18:33

@logikurl ой, в моем ответе были лишние квадратные скобки. это исправлено сейчас

Andrew Parks 24.11.2022 19:49

Попробуй это:

Узнайте больше о Set()

const obj = [{ "category":"e-liquid", "subcategories":[ { "attributes":{ "name":"50ml", "id":19 } }, { "attributes":{ "name":"100ml", "id":18 } }, ], }, { "category":"e-liquid", "subcategories":[ { "attributes":{ "name":"50ml", "id":19 } }, { "attributes":{ "name":"100ml", "id":18 } }, ], }, { "category":"e-liquid", "subcategories":[ { "attributes":{ "name":"50ml", "id":19 } } ] }, { "category":"hardware", "subcategories":[ { "attributes":"tanks", "id":15 } ] }, { "category":"hardware", "subcategories":[ { "attributes":"tanks", "id":15 }, { "attributes":"coils", "id":14 }]}];

const subCat = item => item.map(it => it.attributes.name || it.attributes);
const res = obj.reduce((a, {category, subcategories}) => {
if (a[category]) return {...a, [category]: {category, subcategories: [...new Set([...a[category].subcategories, ...subCat(subcategories)])]}}

return {...a, [category]: {category, subcategories: [...new Set([...subCat(subcategories)])]}};
},{});

console.info(Object.values(res));

Спасибо за ваш ответ! Это прекрасно работает, но использование Set более лаконично и читабельно.

logikurl 22.11.2022 19:37

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

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

reduce группирует по категориям и объединяет подкатегории за один проход по массиву. Вторая поездка очищает повторяющиеся подкатегории.

const data = theAPIdata();

const subCatValue = {
  'e-liquid': obj => obj.attributes.name,
  'hardware': obj => obj.attributes
}

const index = data.reduce((acc, product) => {
  acc[product.category] ??= { category: product.category, subcategories: [] };
  // subCatValue[product.category] is a function that plucks the value we need
  // mapping it over the subcategory array produces an array of salient values
  const values = product.subcategories.map(subCatValue[product.category]);
  acc[product.category].subcategories.push(...values)  
  return acc;
}, {});

const values = Object.values(index);

// go through again, removing duplicate subcategories
values.forEach(v => {
  v.subcategories = Array.from(new Set(v.subcategories))
});

console.info(values);

function theAPIdata() {
  return [{
      "category": "e-liquid",
      "subcategories": [{
          "attributes": {
            "name": "50ml",
            "id": 19
          }
        },
        {
          "attributes": {
            "name": "100ml",
            "id": 18
          }
        },
      ],
    },

    {
      "category": "e-liquid",
      "subcategories": [{
          "attributes": {
            "name": "50ml",
            "id": 19
          }
        },
        {
          "attributes": {
            "name": "100ml",
            "id": 18
          }
        },
      ],
    },

    {
      "category": "e-liquid",
      "subcategories": [{
        "attributes": {
          "name": "50ml",
          "id": 19
        }
      }]
    },

    {
      "category": "hardware",
      "subcategories": [{
        "attributes": "tanks",
        "id": 15
      }]
    },

    {
      "category": "hardware",
      "subcategories": [{
          "attributes": "tanks",
          "id": 15
        },
        {
          "attributes": "coils",
          "id": 14
        }
      ]
    }
  ];
}

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

logikurl 22.11.2022 19:23

@logikurl - рад, что у вас все получилось. Как я отметил в ответе, основная проблема, с которой столкнулся ответ, заключалась в обработке несоответствия формы данных между категориями, проблемы, которую вы стираете при редактировании данных в OP.

danh 22.11.2022 21:53

Спасибо, Дэн. Да, это была опечатка с моей стороны, приношу свои извинения, очень острый глаз и отличное решение для несогласованности данных. К счастью, API, который возвращает противоречивые данные, не зарегистрирован, так как это было бы кошмарным топливом!

logikurl 22.11.2022 22:17

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