У меня есть набор объектов (данные о продукте), которые я отсортировал из 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, если это возможно.
@AndrewParks спасибо, что указали на это, сейчас я исправил, это было непреднамеренно, атрибуты - это объект по всем направлениям.
Получите уникальные имена категорий, затем получите уникальные имена атрибутов для каждой из этих категорий:
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 да, он должен вмещать столько категорий, сколько вам нужно. Кстати, если атрибуты всегда являются объектом, вы можете изменить =>a.name??a
просто на =>a.name
Работал как шарм, короткий, резкий и достаточно читаемый. Пометка как ответ Большое спасибо, Андрей. Это очень помогло мне. Я переименовал вa.name
, так как атрибуты да всегда будут объектом, но очень острым глазом и отличным вкладом.
быстрый вопрос - есть ли способ еще больше сгладить массив подкатегорий, поэтому вместо подкатегорий: [ [ ] ] это подкатегории: []
@logikurl ой, в моем ответе были лишние квадратные скобки. это исправлено сейчас
Попробуй это:
Узнайте больше о 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
более лаконично и читабельно.
Мы можем сгруппировать объекты по категориям, создав объект с категориями в его ключах. Упрощение подкатегорий немного сложнее, поскольку структура объектов подкатегорий различается в зависимости от категории.
В приведенном ниже фрагменте я представил 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 - рад, что у вас все получилось. Как я отметил в ответе, основная проблема, с которой столкнулся ответ, заключалась в обработке несоответствия формы данных между категориями, проблемы, которую вы стираете при редактировании данных в OP.
Спасибо, Дэн. Да, это была опечатка с моей стороны, приношу свои извинения, очень острый глаз и отличное решение для несогласованности данных. К счастью, API, который возвращает противоречивые данные, не зарегистрирован, так как это было бы кошмарным топливом!
На вашем входе первый объект имеет атрибуты как объект. В последнем атрибуты представляют собой строку. Это было намеренно?