Nested Array Filter/Mapping - javascript

Probably a basic question, but I've been blocked for a day now on this.
I am trying to get the correct map/filter from the following array:
[{
"id": 1,
"description": "Electric",
"subUtilityTypes": [{
"id": 5,
"description": "Grid"
},
{
"id": 6,
"description": "Solar"
}
]
},
{
"id": 2,
"description": "Gas",
"subUtilityTypes": [{
"id": 7,
"description": "Heating Oil"
},
{
"id": 8,
"description": "Natural Gas"
},
{
"id": 11,
"description": "Propane"
}
]
}
]
I want to get the id and description inside all subUtilityTypes.
This is what I've been trying:
this.options = arr1.map((parent) => {
return {
id: parent.id,
name: parent.subUtilityTypes.flatMap((child) => {
return child.description;
})
};
});
My problem with what I am trying is that instead of creating separate objects I am getting parent id and list of child names like this:
[{
"id": 1,
"name": [
"Grid",
"Solar"
]
},
{
"id": 2,
"name": [
"Heating Oil",
"Natural Gas",
"Propane"
]
}
]
Expected result should look like this:
[{
"id": 5,
"name": [
"Grid"
]
},
{
"id": 6,
"name": [
"Solar"
]
},
{
"id": 7,
"name": [
"Heating Oil"
]
}
]

Firstly use flatMap to get subUtilityTypes, then map entries:
const input = [{id:1,description:"Electric",subUtilityTypes:[{id:5,description:"Grid"},{id:6,description:"Solar"}]},{id:2,description:"Gas",subUtilityTypes:[{id:7,description:"Heating Oil"},{id:8,description:"Natural Gas"},{id:11,description:"Propane"}]}];
const res = input.flatMap(e => e.subUtilityTypes)
.map(e => ({ id: e.id, name: [e.description] }))
console.log(res)
.as-console-wrapper { max-height: 100% !important; top: 0; } /* ignore this */

Map only arr1 by returning only subUtilityTypes and then map it to get the desired result :
arr1.flatMap(item=>item.subUtilityTypes)
.map(item=>({id:item.id,name:[item.description]}))
let arr1=[
{
"id": 1,
"description": "Electric",
"subUtilityTypes": [
{
"id": 5,
"description": "Grid"
},
{
"id": 6,
"description": "Solar"
}
]
},
{
"id": 2,
"description": "Gas",
"subUtilityTypes": [
{
"id": 7,
"description": "Heating Oil"
},
{
"id": 8,
"description": "Natural Gas"
},
{
"id": 11,
"description": "Propane"
}
]
}
]
let options=arr1.flatMap(item=>item.subUtilityTypes).map(item=>({id:item.id,name:[item.description]}))
console.log(options)

You can also use reduce to achieve the same result.
arr.reduce((acc, curr) => [...acc,...curr.subUtilityTypes.map((o) => ({ id: o.id, name: [o.description] })),],[])
const arr = [
{
id: 1,
description: "Electric",
subUtilityTypes: [
{
id: 5,
description: "Grid",
},
{
id: 6,
description: "Solar",
},
],
},
{
id: 2,
description: "Gas",
subUtilityTypes: [
{
id: 7,
description: "Heating Oil",
},
{
id: 8,
description: "Natural Gas",
},
{
id: 11,
description: "Propane",
},
],
},
];
const result = arr.reduce((acc, curr) => [...acc,...curr.subUtilityTypes.map((o) => ({ id: o.id, name: [o.description] })),],[]);
console.log(result);

Related

how to extract and mapping object from array(object) with redundant key inside key

i have following example array(object):
[
{
"id": 1,
"name": "selling",
"detail": [
{
"id": 11,
"name": "sale-report",
"detail": [
{ "id": 111, "name": "sale-report1", "detail": [] },
{ "id": 112, "name": "sale-report2", "detail": [] }
]
}
]
},
{
"id": 2,
"name": "webstore",
"detail": [
{
"id": 11,
"name": "sale-report",
"detail": [
{ "id": 111, "name": "webstore-report1", "detail": [] },
{ "id": 112, "name": "webstore-report2", "detail": [] }
]
}
]
},
{
"id": 2,
"name": "setting",
"detail": [
{
"id": 11,
"name": "general",
"detail": [
{ "id": 111, "name": "setting-general1", "detail": [] },
{ "id": 112, "name": "setting-general2", "detail": [] }
]
}
]
}
]
how to change the array with new format like this
[
{
"id": 1,
"name": "selling",
},
{
"id": 11,
"name": "sale-report"
},
{ "id": 111, "name": "sale-report1" },
{ "id": 112, "name": "sale-report2" },
{
"id": 2,
"name": "webstore",
},
{
"id": 11,
"name": "sale-report",
},
{ "id": 111, "name": "webstore-report1" },
{ "id": 112, "name": "webstore-report2" },
{
"id": 2,
"name": "setting",
},
{
"id": 11,
"name": "general",
},
{ "id": 111, "name": "setting-general1" },
{ "id": 112, "name": "setting-general2" }
]
with the condition that if there is a key "detail" inside object in the branch, it will be mapped as well (assuming unlimited key "detail" inside object inside array)
note: content of detail will be same as parent, but different value
thanks in advance
i tried mapping mannualy with foreach, but i cant figure out if detail key with array(object) has unlimited nesting
Simply, use recursion to do this.
Here is the working demo-
let input = [
{
id: 1,
name: "selling",
detail: [
{
id: 11,
name: "sale-report",
detail: [
{ id: 111, name: "sale-report1", detail: [] },
{ id: 112, name: "sale-report2", detail: [] }
]
}
]
},
{
id: 2,
name: "webstore",
detail: [
{
id: 11,
name: "sale-report",
detail: [
{ id: 111, name: "webstore-report1", detail: [] },
{ id: 112, name: "webstore-report2", detail: [] }
]
}
]
},
{
id: 2,
name: "setting",
detail: [
{
id: 11,
name: "general",
detail: [
{ id: 111, name: "setting-general1", detail: [] },
{ id: 112, name: "setting-general2", detail: [] }
]
}
]
}
];
let result = [];
function parseData(arr) {
arr.forEach((item) => {
result.push({
id: item.id || null,
name: item.name || null
});
if (item.detail && item.detail.length) {
parseData(item.detail);
}
});
return result;
}
let output = parseData(input);
console.log(output);
You could destructure the objects and take detail out of the object and map the rest object and the results of nested details array as flat array with a recursive function.
const
flat = (array = []) => array.flatMap(({ detail, ...rest }) => [
rest,
...flat(detail)
]),
data = [{ id: 1, name: "selling", detail: [{ id: 11, name: "sale-report", detail: [{ id: 111, name: "sale-report1", detail: [] }, { id: 112, name: "sale-report2", detail: [] }] }] }, { id: 2, name: "webstore", detail: [{ id: 11, name: "sale-report", detail: [{ id: 111, name: "webstore-report1", detail: [] }, { id: 112, name: "webstore-report2", detail: [] }] }] }, { id: 2, name: "setting", detail: [{ id: 11, name: "general", detail: [{ id: 111, name: "setting-general1", detail: [] }, { id: 112, name: "setting-general2", detail: [] }] }] }],
result = flat(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A non recursive method of doing this could look something like the following:
const data = [{"id":1,"name":"selling","detail":[{"id":11,"name":"sale-report","detail":[{"id":111,"name":"sale-report1","detail":[]},{"id":112,"name":"sale-report2","detail":[]}]}]},{"id":2,"name":"webstore","detail":[{"id":11,"name":"sale-report","detail":[{"id":111,"name":"webstore-report1","detail":[]},{"id":112,"name":"webstore-report2","detail":[]}]}]},{"id":2,"name":"setting","detail":[{"id":11,"name":"general","detail":[{"id":111,"name":"setting-general1","detail":[]},{"id":112,"name":"setting-general2","detail":[]}]}]}];
const extract = (data) => {
const result = [];
const queue = [...data];
while(queue.length > 0) {
const { detail, ...rest } = queue.shift();
result.push(rest);
queue.unshift(...detail);
}
return result;
};
console.log(extract(data));

Filter complex array in react native?

I have array .
const arr = [{
"status": "success",
"data": [{
"name": "user1",
"games": [{
"id": 1,
"gamename": "cricket"
}, {
"id": 2,
"gamename": "football"
}]
},
{
"name": "user1",
"games": [{
"id": 1,
"gamename": "videogames"
}, {
"id": 2,
"gamename": "volleyball"
}]
}
]
}]
I tried following the code to filter it. and no output show
arr.map((item,idx) => (
console.log(item.data.games.gamename)
)
))
I want to print all game name eg.
cricket
football
videogames
volleyball
We can use flatMap() to do it
const arr = [{
"status": "success",
"data": [{
"name": "user1",
"games": [{
"id": 1,
"gamename": "cricket"
}, {
"id": 2,
"gamename": "football"
}]
},
{
"name": "user1",
"games": [{
"id": 1,
"gamename": "videogames"
}, {
"id": 2,
"gamename": "volleyball"
}]
}
]
}]
// multiple flatMap chain invocation seems ugly,waiting for more elegant solution
let result = arr.flatMap(a => a.data).flatMap(a => a.games).flatMap(a => a.gamename)
console.log(result)
Data is a array and so is games:
const arr = [
{
status: "success",
data: [
{
name: "user1",
games: [
{
id: 1,
gamename: "cricket",
},
{
id: 2,
gamename: "football",
},
],
},
{
name: "user1",
games: [
{
id: 1,
gamename: "videogames",
},
{
id: 2,
gamename: "volleyball",
},
],
},
],
},
];
arr.map((item) => {
item.data.map((item) => {
item.games.map((item) => {
console.log(item.gamename);
});
});
});
Try out this code, it will return only game names, you can change the join if don't need comma (,)
Output :
"cricket,football,videogames,volleyball"
const arr = [{
"status": "success",
"data": [{
"name": "user1",
"games": [{
"id": 1,
"gamename": "cricket"
}, {
"id": 2,
"gamename": "football"
}]
},
{
"name": "user1",
"games": [{
"id": 1,
"gamename": "videogames"
}, {
"id": 2,
"gamename": "volleyball"
}]
}
]
}];
console.log(JSON.stringify(arr.filter(e=>e.status=="success").map(e=>e.data.map(f=>f.games.map(g=>g.gamename)).join(",")).join(",")));

JavaScript: How to Group Array of Objects with Nested Properties?

I have this array of objects:
[
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{
"item_id": 1,
"item": {
"item_name": "Item 1",
},
"count": 1
}
]
},
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{
"item_id": 1,
"item": {
"item_name": "Item 1",
},
"count": 2
},
{
"item_id": 2,
"item": {
"item_name": "Item 2",
},
"count": 1
}
]
}
]
What I wanted to achieve is to group by date, name and the number of items. The desired result would be:
[
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{
"item_id": 1,
"name": "Item 1",
"count": 3
},
{
"item_id": 2,
"name": "Item 2",
"count": 1
}
]
}
]
If it has the same date but different name like:
[
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{
"item_id": 1,
"item": {
"item_name": "Item 1",
},
"count": 1
}
]
},
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{
"item_id": 1,
"item": {
"item_name": "Item 1",
},
"count": 2
},
{
"item_id": 2,
"item": {
"item_name": "Item 2",
},
"count": 1
}
]
},
{
"date": "08/08/2022",
"name": "Archer",
"items": [
{
"item_id": 1,
"item": {
"item_name": "Item 1",
},
"count": 2
},
{
"item_id": 2,
"item": {
"item_name": "Item 2",
},
"count": 1
}
]
}
]
The desired result would be:
[
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{
"item_id": 1,
"name": "Item 1",
"count": 3
},
{
"item_id": 2,
"name": "Item 2",
"count": 1
}
]
},
{
"date": "08/08/2022",
"name": "Archer",
"items": [
{
"item_id": 1,
"name": "Item 1",
"count": 2
},
{
"item_id": 2,
"name": "Item 2",
"count": 1
}
]
}
]
I've tried using lodash and array reduce method but still unable to get the desired result:
const groups = data.reduce((groups, data) => {
const date = data.date;
if (!groups[date]) {
groups[date] = [];
}
groups[date].push(data);
return groups;
}, {});
const groupArrays = Object.keys(groups).map((date) => {
return {
date,
items: groups[date]
};
});
How to achieve this one? Need your inputs. Thanks!
You can achieve this as:
const arr = [
{
date: '08/08/2022',
name: 'Swordsman',
items: [
{
item_id: 1,
item: {
item_name: 'Item 1',
},
count: 1,
},
],
},
{
date: '08/08/2022',
name: 'Swordsman',
items: [
{
item_id: 1,
item: {
item_name: 'Item 1',
},
count: 2,
},
{
item_id: 2,
item: {
item_name: 'Item 2',
},
count: 1,
},
],
},
{
date: '08/08/2022',
name: 'Archer',
items: [
{
item_id: 1,
item: {
item_name: 'Item 1',
},
count: 2,
},
{
item_id: 2,
item: {
item_name: 'Item 2',
},
count: 1,
},
],
},
];
function addItemsToNewArrFromOld(oldItems, newItemsToAdd) {
newItemsToAdd.forEach((o) => {
const { item_id, count, item: { item_name: item }} = o;
const itemInOldItems = oldItems.find((obj) => obj.item_id === o.item_id);
if (itemInOldItems) itemInOldItems.count += o.count;
else oldItems.push({ item_id, count, item });
});
}
const map = new Map();
arr.forEach(({ date, name, items }) => {
const key = `${date}-${name}`;
if (map.has(key)) {
const objForKeyFound = map.get(key);
addItemsToNewArrFromOld(objForKeyFound.items, items);
} else {
map.set(key, {
date, name, items: items.map(({ item_id, item, count }) => ({ item_id, count, item: item.item_name })),
});
}
});
const result = [...map.values()];
console.log(result);
Using Array#reduce, iterate over the list while updating a Map where the key is <date>-<name> and the value is the grouped object (having all items of this key).
Using Array#map, iterate over the above grouped objects. For each object, we need to group its items by item_id while updating the count.
const groupByDateAndName = arr => [...
arr.reduce((map, { date, name, items = [] }) => {
const key = `${date}-${name}`;
const { items: prevItems = [] } = map.get(key) ?? {};
map.set(
key,
{
date,
name,
items: [
...prevItems,
...items.map(({ item_id, item, count }) => ({ item_id, count, name: item.item_name }))
]
}
);
return map;
}, new Map)
.values()
];
const groupItemsById = arr => arr.map(e => {
const items = [...
e.items.reduce((map, { item_id, name, count }) => {
const { count: prevCount = 0 } = map.get(item_id) ?? {};
map.set(item_id, { item_id, name, count: prevCount + count })
return map;
}, new Map)
.values()
]
return { ...e, items };
});
const group = (arr = []) => {
const list = groupByDateAndName(arr);
return groupItemsById(list);
}
const arr = [
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{ "item_id": 1, "item": { "item_name": "Item 1" }, "count": 1 }
]
},
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{ "item_id": 1, "item": { "item_name": "Item 1" }, "count": 2 },
{ "item_id": 2, "item": { "item_name": "Item 2" }, "count": 1 }
]
},
{
"date": "08/08/2022",
"name": "Archer",
"items": [
{ "item_id": 1, "item": { "item_name": "Item 1" }, "count": 2 },
{ "item_id": 2, "item": { "item_name": "Item 2" }, "count": 1 }
]
}
];
console.log( group(arr) );

Array filter returning entire array

I'm trying to filter an array with nested objects like this:
const items = [
{
"id": 1,
"name": "test1",
"subitems": [
{
"id": 2,
"name": "test2",
"subsubitems": [
{
"id": 3,
"name": "test3",
},
{
"id": 4,
"name": "test4",
}
]
}
]
},
{
"id": 10,
"name": "test10",
"subitems": [
{
"id": 20,
"name": "test20",
"subsubitems": [
{
"id": 30,
"name": "test30",
}
]
}
]
}
]
const filteredResults = items.filter((item) =>
item.subitems.filter((subitem) =>
subitem.subsubitems.filter((subsubitem) =>
subsubitem.name.includes('test3')
)
)
)
console.log(filteredResults)
But it's not filtering correctly, the original array is being returned. Right now I'm just attempting to filter at the subsubitems level. Ultimately I want to return an array with any matches to name at any level.
So test2 would return an array like this:
[
{
"id": 1,
"name": "test1",
"subitems": [
{
"id": 2,
"name": "test2",
"subsubitems": [
{
"id": 3,
"name": "test3",
},
{
"id": 4,
"name": "test4",
}
]
}
]
}
]
And test3 would return an array like this:
[
{
"id": 1,
"name": "test1",
"subitems": [
{
"id": 2,
"name": "test2",
"subsubitems": [
{
"id": 3,
"name": "test3",
}
]
}
]
}
]
And test would return everything.
Try This:
const items = [
{
id: 1,
name: "test1",
subitems: [
{
id: 2,
name: "test2",
subsubitems: [
{
id: 3,
name: "test3",
},
{
id: 4,
name: "test4",
},
],
},
],
},
{
id: 10,
name: "test10",
subitems: [
{
id: 20,
name: "test20",
subsubitems: [
{
id: 30,
name: "test30",
},
],
},
],
},
];
const filterItems = (items, filter) => {
const result = [];
items.forEach((item) => {
if (item.name === filter) {
result.push(item);
}
item.subitems.forEach((subitem) => {
if (subitem.name === filter) {
result.push(item);
}
subitem.subsubitems.forEach((subsubitem) => {
if (subsubitem.name === filter) {
result.push(item);
}
});
});
});
console.log("filtered", result);
};
filterItems(items, "test10");
Result is :
filtered [ { id: 10, name: 'test10', subitems: [ [Object] ] } ]

Normalizing Nested Many To Many

I am looking to find a way to normalize nested many to many relationships.
However, it has been going when the model is a few levels deep, such as Categories in the example below. In the output the location id 27 only contains category ids 1 and 7 (which I assume is because that is what the last version of location 27 shows). What is the best approach in normalizing this type of data?
Would using shop id in the location be a good idea? i.e.
"locations": {
"1-27": {
"id": 27,
"label": "Birmingham",
"categories": [
1,
2,
7
]
},
"2-27": {
"id": 27,
"label": "Birmingham",
"categories": [
1,
7
]
}
},
Thanks for taking the time to read this!
Additional Data Details
Database
Shops
id
label
Locations
id
label
Location_Shop
location_id
shop_id
Categories
id
label
Category_Location
category_id
location_id
Example Data
[
{
id: 1,
label: 'First Shop',
locations: [
{
id: 27,
label: 'Birmingham',
categories: [
{
id: 1,
label: 'Car Park',
},
{
id: 2,
label: 'Petrol Station',
},
{
id: 7,
label: 'Bakery',
},
],
},
],
},
{
id: 2,
label: 'Second Shop',
locations: [
{
id: 27,
label: 'Birmingham',
categories: [
{
id: 1,
label: 'Car Park',
},
{
id: 7,
label: 'Bakery',
},
],
},
],
},
]
Code
const categorySchema = new schema.Entity('categories');
const locationSchema = new schema.Entity('locations', {
categories: [categorySchema],
});
const shopSchema = new schema.Entity('shops', {
locations: [locationSchema],
});
Output
{
"entities": {
"categories": {
"1": {
"id": 1,
"label": "Car Park"
},
"2": {
"id": 2,
"label": "Petrol Station"
},
"7": {
"id": 7,
"label": "Bakery"
}
},
"locations": {
"27": {
"id": 27,
"label": "Birmingham",
"categories": [
1,
7
]
}
},
"shops": {
"1": {
"id": 1,
"label": "First Shop",
"locations": [
27
]
},
"2": {
"id": 2,
"label": "Second Shop",
"locations": [
27
]
}
}
},
"result": [
1,
2
]
}

Categories