Получить из одного массива другой по хитрой логике
Есть массив:
arr1 = [
{
person_id: 1,
fullName: "Иван",
position_id: 01,
position_name: "стажер",
group_id: 001
},
{
person_id: 2,
fullName: "Марья",
position_id: 02,
position_name: "продавец",
group_id: 002
},
{
person_id: 3,
fullName: "Сергей",
position_id: 03,
position_name: "директор",
group_id: 002
},
{
person_id: 4,
fullName: "Василиса",
position_id: 02,
position_name: "продавец",
group_id: 002
},
{
person_id: 5,
fullName: "Юрий",
position_id: 01,
position_name: "стажер",
group_id: 002
},
{
person_id: 6,
fullName: "Ольга",
position_id: 02,
position_name: "продавец",
group_id: 002
}
];
должен получиться следующий массив:
arrResult = [
{
group_id: 001,
groupData: [
{
position_id: 01,
position_name: "стажер",
persons: [
{
person_id: 1,
fullName: "Иван"
}
]
}
]
},
{
group_id: 002,
groupData: [
{
position_id: 01,
position_name: "стажер",
persons: [
{
person_id: 5,
fullName: "Юрий"
}
]
},
{
position_id: 02,
position_name: "продавец",
persons: [
{
person_id: 2,
fullName: "Марья"
},
{
person_id: 4,
fullName: "Василиса"
},
{
person_id: 6,
fullName: "Ольга"
}
]
},
{
position_id: 03,
position_name: "директор",
persons: [
{
person_id: 3,
fullName: "Сергей"
}
]
}
]
}
];
Я попробовал сделать это следующей формулой:
const result = Object.values(data.reduce((accum, item) => (
(item.group_id in accum) ? '' : accum[item.group_id] = {
'group_id': item.group_id,
'group_name': item.group_name
}, accum
), {}))
Что в целом позволяет мне сделать массив образца:
[
{group_id: xxx, group_name: xxx, ...},
{group_id: xxx, group_name: xxx, ...},
{group_id: xxx, group_name: xxx, ...}
]
Но я не понимаю, как внутри reducer`a формировать еще и массивы и забирать из главного массива данные... Если кто-то поможет осознать, как сделать - буду мега благодарен
Ответы (3 шт):
Думаю такой вариант пойдёт.
const arr1 = [
{"person_id": 1, "fullName": "Иван", "position_id": 01, "position_name": "стажер", "group_id": 001},
{"person_id": 2, "fullName": "Марья", "position_id": 02, "position_name": "продавец", "group_id": 002},
{"person_id": 3, "fullName": "Сергей", "position_id": 03, "position_name": "директор", "group_id": 002},
{"person_id": 4, "fullName": "Василиса", "position_id": 02, "position_name": "продавец", "group_id": 002},
{"person_id": 5, "fullName": "Юрий", "position_id": 01, "position_name": "стажер", "group_id": 002},
{"person_id": 6, "fullName": "Ольга", "position_id": 02, "position_name": "продавец", "group_id": 002}
];
let arrResult = []; // Хранить "форматированный" массив будем тут (далее буду называть его "возвращаемым")
arr1.map(e => { // Проходим циклом по массиву
let group = arrResult.find(s => s.group_id === e.group_id); // Проверяем, есть ли уже в возращаемом объект с `group_id` ранвный тому, что в шаге цикла.
if(!group) { // Если нет
arrResult.push({ // То создаём запись
group_id: e.group_id,
groupData: []
});
group = arrResult.find(s => s.group_id === e.group_id); // Переназначаем переменную
}
// Далее аналогично, только для позиции
let position = group.groupData.find(s => s.position_name === e.position_name);
if(!position) {
group.groupData.push({
position_id: e.position_id,
position_name: e.position_name,
persons: []
})
position = group.groupData.find(s => s.position_name === e.position_name);
}
// А тут уже добавляем человека
position.persons.push({
person_id: e.person_id,
fullName: e.fullName
})
});
console.info(arrResult);
Одно "но", различия только в сортировке, думаю если это важно, то проще пройтись ещё сортировкой поверх.
Основная идея заключается в том чтобы сразу формировать нужный массив, в качестве позиций во всех массивах использовать id-1. Так как JS позволяет создавать элемент массива в любой позиции не зависимо от размера массива, то будут образовываться пустые элементы (null) которые потом можно отсеять фильтром.
Плюс данного подхода в том что практически за один проход выполняется и сортировка элементов и не используется метод массива find, что возможно скажется на скорости работы.
const data = [
{
person_id: 1,
fullName: "Иван",
position_id: 01,
position_name: "стажер",
group_id: 001
},
{
person_id: 2,
fullName: "Марья",
position_id: 02,
position_name: "продавец",
group_id: 002
},
{
person_id: 3,
fullName: "Сергей",
position_id: 03,
position_name: "директор",
group_id: 002
},
{
person_id: 4,
fullName: "Василиса",
position_id: 02,
position_name: "продавец",
group_id: 002
},
{
person_id: 5,
fullName: "Юрий",
position_id: 01,
position_name: "стажер",
group_id: 002
},
{
person_id: 6,
fullName: "Ольга",
position_id: 02,
position_name: "продавец",
group_id: 002
}
];
const result = data.reduce((accum, item) => {
// если нет группы создаем
if (!accum[item.group_id-1])
accum[item.group_id-1] = {
group_id: item.group_id,
group_Data: []
}
// если нет данных группы создаем
if (!accum[item.group_id-1].group_Data[item.position_id-1])
accum[item.group_id-1]
.group_Data[item.position_id-1] =
{
position_id: item.position_id,
position_name: item.position_name,
persons: []
}
// добавляем персон
//if (!accum[item.group_id-1].group_Data[item.position_id-1]
// .persons[item.person_id-1])
accum[item.group_id-1]
.group_Data[item.position_id-1]
.persons[item.person_id-1] =
{
person_id: item.person_id,
fullName: item.fullName
}
// удаляем образованные дыры в массивах
accum[item.group_id-1].group_Data[item.position_id-1]
.persons = accum[item.group_id-1].group_Data[item.position_id-1]
.persons.filter(e => e != null)
return accum
},[])
console.log(JSON.stringify(result, null, 2))
Никак не хочу задеть тех, кто уже до этого ответили, каждый из ответ по своему удобен и логичен. Но лично для меня они не приемлимы.
Объясню, почему:
- В ответе
Daniil Lobanиспользуется то чтоidпредставляет собой номер или строку, которая может быть представлен как номер и на этом завязана основная логика, что как по мне неправильно, т.к.id- это не обязательно номер, может быть и набором других символов, что не редкость. Но опять таки для данного конкретного случая - это рабочий, удобный и понятный вариант, который так же позволят не сортировать массив (но и то только если нужно отсортировать по возрастанию). Но даже с учётом этогоidмогут быть большими числами с большими разницами, что приведёт к огромному кол-ву пустых элементов, от которых надо отдельно избавляться, а так же, еслиid- это строка цифр, то нет гарантии, что не придут 2 элемента, где у одногоid- это к примеру'2', а у другого'02'. В таком случае один элемент перетерёт другой, что тоже неправильно - В ответе
De.Minovнет тех проблем, что присутствуют в другом ответе, но используется многоfind, что для мелких массивов никакой существенной разницы не имеет, но для больших данных - это будет заметно влиять на скорость
Алгоритм, который я предложу ниже, не содержит указанных выше минусов, но будет использовать больше объёма памяти, чем предыдущие алгоритмы, что так же может стать проблемой в каких-то случаях. Но как по мне, нынешние устройства, имеют достаточный объём памяти, чтобы я мог позаимствовать часть, для ускорения выполнения алгоритма. Ещё одним из минусов - это то что "магической" сортировки на месте как у Daniil Loban тут нет, потому надо будет отдельно сортировать массив, что более гибко. Ведь сортировка не обязана быть по возрастанию и не обязана быть именно по id, тем более каждый массив может понадобиться отсортировать по своёму ключу и порядку.
Основная идея будет заключена в том, чтобы свести поиск нахождения того или иного объекта в массиве к сложности O(1) путём создания "карты" массива в отдельной переменной. Как ключ в "карте" будет использовано id элемента, если такого нет, то можно составить для каждого элемента уникальную строку, основываясь на данных элемента, если, конечно, не будут два абсолютно одинковых элементов :) Но если есть риск, такого, то к ключу можно добавить что-то, например номер в массиве. Там уже по ситуации надо смотреть
И так сам код:
const data = [{
person_id: 1,
fullName: "Иван",
position_id: 01,
position_name: "стажер",
group_id: 001
},
{
person_id: 2,
fullName: "Марья",
position_id: 02,
position_name: "продавец",
group_id: 002
},
{
person_id: 3,
fullName: "Сергей",
position_id: 03,
position_name: "директор",
group_id: 002
},
{
person_id: 4,
fullName: "Василиса",
position_id: 02,
position_name: "продавец",
group_id: 002
},
{
person_id: 5,
fullName: "Юрий",
position_id: 01,
position_name: "стажер",
group_id: 002
},
{
person_id: 6,
fullName: "Ольга",
position_id: 02,
position_name: "продавец",
group_id: 002
}
];
const formattedData = data.reduce((groupsData, el) => {
const {groupsMap, groupsArr} = groupsData;
if (!groupsMap.has(el.group_id)) {
groupsMap.set(el.group_id, {
map: new Map(),
index: groupsArr.length
});
groupsArr.push({
group_id: el.group_id,
groupData: []
});
}
const groupData = groupsMap.get(el.group_id);
const positionsMap = groupData.map;
const positionsArr = groupsArr[groupData.index].groupData;
if (!positionsMap.has(el.position_id)) {
positionsMap.set(el.position_id, {
set: new Set(),
index: positionsArr.length
});
positionsArr.push({
position_id: el.position_id,
position_name: el.position_name,
persons: []
});
}
const positionData = positionsMap.get(el.position_id);
const personsSet = positionData.set;
const personsArr = positionsArr[positionData.index].persons;
if (!personsSet.has(el.person_id)) {
personsSet.add(el.person_id);
personsArr.push({
person_id: el.person_id,
fullName: el.fullName
});
}
return groupsData;
}, {
groupsMap: new Map(),
groupsArr: []
}).groupsArr;
console.log(formattedData);
Буду рад конструктивной критике минусов моего кода, кроме тех, которые я сам уже указал