Remodel array of object using reduce - javascript

I want to change the structure of the current Array that I am consuming for a better use case, I want to group items into subarrays in this order. soccer > segment name > league > fixtures . I have been stuck on this for hours. I tried to use reduce but none of my attempts was close to success. Below is the sample data and the result I am trying to achieve.
const currentData = [
{
"fixtureName": "Fulham vs Chelsea",
"league": "Premier League",
"sport": "Soccer",
"segmentName": "England",
"markets": []
},
{
"fixtureName": "Arsenal vs liverpool",
"league": "Premier League",
"sport": "Soccer",
"segmentName": "England",
"markets": []
}, {
"fixtureName": "Middlesbrough FC vs. West Bromwich Albion",
"league": "Championship",
"sport": "Soccer",
"segmentName": "England",
"markets": []
}, {
"fixtureName": "Club El Porvenir vs. Berazategui",
"league": "Primera C",
"sport": "Soccer",
"segmentName": "Argentina",
"markets": []
}, {
"fixtureName": "Lille vs Angers ",
"league": "Ligue 1",
"sport": "Soccer",
"segmentName": "France",
"markets": []
}, {
"fixtureName": "PSG vs Nantes",
"league": "Ligue 1",
"sport": "Soccer",
"segmentName": "France",
"markets": []
}, {
"fixtureName": "Argentino de Quilmes vs. Comunicaciones",
"league": "Primera B",
"sport": "Soccer",
"segmentName": "Argentina",
"markets": []
}, {
"fixtureName": "Deportivo Merlo vs. Club Atletico Fenix",
"league": "Primera B",
"sport": "Soccer",
"segmentName": "Argentina",
"markets": []
},
]
This is the type of output and result I want
const data = [
{
sportID: 1,
sportName: 'Soccer',
categories: [
{
itemName: 'Argentina',
tournaments: [
{
itemID: 1,
itemName: 'ARG - Primera B',
events: [
{
id: 1,
itemName: 'Argentino de Quilmes vs. Comunicaciones',
markets: [],
},
{
id: 2,
itemName: 'Deportivo Merlo vs. Club Atletico Fenix',
markets: [],
},
],
},
{
itemID: 2,
itemName: 'ARG - Primera C',
events: [
{
id: 1,
itemName: 'Club El Porvenir vs. Berazategui',
markets: [],
},
],
},
],
},
{
itemName: 'England',
tournaments: [
{
itemID: 1,
itemName: 'ENG - Premier League',
events: [
{
id: 1,
itemName: 'Arsenal vs Liverpool',
markets: [],
},
{
id: 2,
itemName: 'Fulham vs Chelsea',
markets: [],
},
],
},
{
itemID: 2,
itemName: 'ENG - Championship',
events: [
{
id: 1,
itemName: 'Middlesbrough FC vs. West Bromwich Albion',
markets: [],
},
],
},
],
},
{
itemName: 'France',
tournaments: [
{
itemID: 1,
itemName: 'Ligue 1',
events: [
{
id: 1,
itemName: 'PSG vs Nantes',
markets: [],
},
{
id: 2,
itemName: 'Lille vs Angers',
markets: [],
},
],
},
],
},
],
},
];

The expected results don't quite match the input, but here's a way to reduce each item to its location in the tree, by first creating the tree's objects if not already created.
var currentData = [{"fixtureName":"Fulham vs Chelsea","league":"Premier League","sport":"Soccer","segmentName":"England","markets":[]},{"fixtureName":"Arsenal vs liverpool","league":"Premier League","sport":"Soccer","segmentName":"England","markets":[]},{"fixtureName":"Middlesbrough FC vs. West Bromwich Albion","league":"Championship","sport":"Soccer","segmentName":"England","markets":[]},{"fixtureName":"Club El Porvenir vs. Berazategui","league":"Primera C","sport":"Soccer","segmentName":"Argentina","markets":[]},{"fixtureName":"Lille vs Angers ","league":"Ligue 1","sport":"Soccer","segmentName":"France","markets":[]},{"fixtureName":"PSG vs Nantes","league":"Ligue 1","sport":"Soccer","segmentName":"France","markets":[]},{"fixtureName":"Argentino de Quilmes vs. Comunicaciones","league":"Primera B","sport":"Soccer","segmentName":"Argentina","markets":[]},{"fixtureName":"Deportivo Merlo vs. Club Atletico Fenix","league":"Primera B","sport":"Soccer","segmentName":"Argentina","markets":[]}]
var obj = currentData.reduce(function(agg, item) {
agg[item.sport] ??= {}
agg[item.sport][item.segmentName] ??= {}
agg[item.sport][item.segmentName][item.league] ??= {}
agg[item.sport][item.segmentName][item.league][item.fixtureName] ??= []
agg[item.sport][item.segmentName][item.league][item.fixtureName].push(item)
return agg;
}, {});
console.log(obj)
.as-console-wrapper {
max-height: 100% !important;
}

Related

format object array to grouped nested array object to generate antd tree table

I get the following array object from response and I need to implement antd tree table in my react application using this array object.
const students=[
{
"index": "5260574",
"studentName": "David Sinclair",
"marks": "177",
"gender": "M",
"resultId": 16,
"province": {
"code": "1",
"name": "Andregan"
},
"county": {
"name": "Silk",
"code": "21"
},
"city": {
"code": "234",
"name": "Meegan"
},
"rural": {
"code": "41",
"name": "Thumphil"
},
"canEvaluate": true
},
{
"index": "5259991",
"studentName": "KALURA GEETH DAMS",
"marks": "177",
"gender": "M",
"resultId": 17,
"province": {
"code": "1",
"name": "Andregan"
},
"county": {
"name": "Silk",
"code": "21"
},
"city": {
"code": "234",
"name": "Meegan"
},
"rural": {
"code": "41",
"name": "Thumphil"
},
"canEvaluate": true
},
{
"index": "5260534",
"studentName": "Mathew Jenkins",
"marks": "157",
"gender": "M",
"resultId": 21,
"province": {
"code": "1",
"name": "Andregan"
},
"county": {
"name": "Silk",
"code": "21"
},
"city": {
"code": "44",
"name": "Wrens"
},
"rural": {
"code": "49",
"name": "Cardin"
},
"canEvaluate": true
},
{
"index": "5260680",
"studentName": "MEDIW KENULA",
"marks": "177",
"gender": "M",
"resultId": 18,
"province": {
"code": "2",
"name": "Kiren"
},
"county": {
"name": "Georgia",
"code": "23"
},
"city": {
"code": "34",
"name": "Eden Hill"
},
"rural": {
"code": "99",
"name": "Southern Sea"
},
"canEvaluate": true
}
]
I want to group this response as province , county, city, rural wise as shown in below rendered antd tree table.
.
To get above tree table and get selected students, I need to format the response as below for the dataSource.
Expected output is as below.
const data = [
{
key: '1',
name: 'Andregan',
resultCount: 3,
resultIds: [16, 17, 21],
children: [
{
key: '21',
name: 'Silk',
resultCount: 2,
resultIds: [16, 17, 21],
children: [
{
key: '234',
name: 'Meegan',
resultCount: 2,
resultIds: [16, 17],
children: [
{
key: '41',
name: 'Thumphil',
resultCount: 2,
resultIds: [16, 17],
},
],
},
{
key: '44',
name: 'Wrens',
resultCount: 1,
resultIds: [21],
children: [
{
key: '49',
name: 'Cardin',
resultCount: 1,
resultIds: [21],
},
],
},
],
},
],
},
{
key: '2',
name: 'Kiren',
resultCount: 1,
resultIds: [18],
children: [
{
key: '23',
name: 'Georgia',
resultCount: 1,
resultIds: [18],
children: [
{
key: '34',
name: 'Eden Hill',
resultCount: 1,
resultIds: [18],
children: [
{
key: '99',
name: 'Southern Sea',
resultCount: 1,
resultIds: [18],
},
],
},
],
},
],
},
];
Can anyone suggest how to format response to above format to achieve this? Thank you.

Change object structure Javascript

I have an array and I want to override the object attributes
This the main data
const Data = {
"id": "1",
"name": "USA",
"questions": [{ id: 1, name: "1 qst" }, { id: 2, name: "2 qst" }, { id: 3, name: "3 qst" }],
"children": [
{ "id": "1" , "name": "DC" ,"questions": [{ id: 1, name: "1 qst" }, { id: 2, name: "2 qst" }, { id:2, name: "3 qst" }]},
{ "id": "2" , "name": "Florida" ,"questions": [{ id: 1, name: "1 qst" }, { id: 2, name: "2 qst" }, { id: 3, name: "3 qst" }]}
]
}
I want to change in every question instead of name I want to put questionName like this
{ id: 1, questionName: "1 qst" }
I was able to change it in first object question through this code
let dataFiltred = Data[0]?.questions?.map((item) => {
return {
questionName: item.name,
id: item.id,
}
})
But I am struggling to change it in children question
function mapQuestionObject({ name, id }) {
return { id, questionName: name };
}
const mapped = {
...Data,
questions: Data.questions.map(mapQuestionObject),
children: Data.children.map(child => ({
...child,
questions: child.questions.map(mapQuestionObject),
}),
};
Map each questions array to a new array and change the name property in the mapped value.
const data = {
"id": "1",
"name": "USA",
"questions": [{ id: 1, name: "1 qst" }, { id: 2, name: "2 qst" }, { id: 3, name: "3 qst" }],
"children": [
{ "id": "1" , "name": "DC" ,"questions": [{ id: 1, name: "1 qst" }, { id: 2, name: "2 qst" }, { id:2, name: "3 qst" }]},
{ "id": "2" , "name": "Florida" ,"questions": [{ id: 1, name: "1 qst" }, { id: 2, name: "2 qst" }, { id: 3, name: "3 qst" }]}
]
};
const newData = {
...data,
questions: data.questions.map(({ name: questionName, ...rest }) => ({
...rest,
questionName,
})),
children: data.children.map(child => ({
...child,
questions: child.questions.map(({ name: questionName, ...rest }) => ({
...rest,
questionName,
}))
})),
};
console.log(newData);
Since the questions mapping is the same callback you can factor it out to make your code more DRY
const data = {
"id": "1",
"name": "USA",
"questions": [{ id: 1, name: "1 qst" }, { id: 2, name: "2 qst" }, { id: 3, name: "3 qst" }],
"children": [
{ "id": "1" , "name": "DC" ,"questions": [{ id: 1, name: "1 qst" }, { id: 2, name: "2 qst" }, { id:2, name: "3 qst" }]},
{ "id": "2" , "name": "Florida" ,"questions": [{ id: 1, name: "1 qst" }, { id: 2, name: "2 qst" }, { id: 3, name: "3 qst" }]}
]
};
const mapQuestions = arr => arr.map(({ name: questionName, ...rest }) => ({
...rest,
questionName,
}));
const newData = {
...data,
questions: mapQuestions(data.questions),
children: data.children.map(child => ({
...child,
questions: mapQuestions(child.questions),
})),
};
console.log(newData);

What is the best way to group this js array items in this structure

how can i convert returned data from this structure:
[
{
"id": 91,
"name": "Art",
"division": {
"id": 4,
"name": "1st level",
"created_at": "2018-11-05T10:11:37+00:00",
},
"created_at": "2018-11-05T10:11:37+00:00",
},
{
"id": 188,
"name": "English",
"division": {
"id": 9,
"name": "3rd level",
"created_at": "2018-11-05T10:11:37+00:00",
},
"created_at": "2018-11-05T10:11:37+00:00",
},
{
"id": 218,
"name": "Art",
"division": {
"id": 11,
"name": "3rd level",
"created_at": "2018-11-05T10:11:37+00:00",
},
"created_at": "2018-11-05T10:11:37+00:00",
}
]
to this structure :
[
{
"id": 1,
"name": "Art",
"classes": [
{
"id": 91,
"name": "1st level",
},
{
"id": 218,
"name": "3rd level",
},
],
},
{
"id": 2,
"name": "English",
"classes": [
{
"id": 188,
"name": "3rd level",
},
],
},
]
note: class.id = parentSubject.id
I wrote some codes to solve the problem but I'm looking for the best way !!
i use .reduce() function
I will attach the codes in the comments box.
Here is how I would do it:
let ans = initialArray.reduce((cum,x) => {
let foundIndex = cum.findIndex((a) =>{
return a.name == x.name});
if(foundIndex!=-1){
cum[foundIndex].classes.push({
id : x.id,
name : x.division.name
})
}
else{
cum.push({
id : cum.length+1,
name : x.name,
classes : [{
id : x.id,
name : x.division.name
}]
})
}
return cum;
},[]);
I use reduce and findIndex methods.
You can use array map to create a new array of objects with the new id.
const oldDatas = [
{
id: 91,
name: 'Art',
division: {
id: 4,
name: '1st level',
created_at: '2018-11-05T10:11:37+00:00',
},
created_at: '2018-11-05T10:11:37+00:00',
},
{
id: 188,
name: 'English',
division: {
id: 9,
name: '3rd level',
created_at: '2018-11-05T10:11:37+00:00',
},
created_at: '2018-11-05T10:11:37+00:00',
},
{
id: 218,
name: 'Art',
division: {
id: 11,
name: '3rd level',
created_at: '2018-11-05T10:11:37+00:00',
},
created_at: '2018-11-05T10:11:37+00:00',
},
];
const newDatas = oldDatas.map((data, index) => {
return { ...data, id: index + 1 };
});
console.log(newDatas);
my solution :
let res = initialArray.reduce((acc, obj) => {
const exist = acc.findIndex((item) => item.name === obj.name);
if (exist >= 0) {
acc[exist] = {
id: exist,
name: obj.name,
classes: [
...acc[exist].classes,
{ id: obj.id, name: obj.division.name },
],
};
} else {
acc.push({
id: acc.length,
name: obj.name,
classes: [{ id: obj.id, name: obj.division.name }],
});
}
return acc;
}, []);

Reorganization and cleaning of a 'treeview' JSON object

I want to create a JavaScript method that transforms a Json ‘treeView’ object. My starting object has a structure with duplicated branches:
Example of basic data:
- ROOT
- 01
- 011
- 0111
- 01
- 011
- 0112
- 01
- 011
- 0113
- 01
- 012
- 0121
- 01
- 012
- 0122
- 01
- 012
- 0123
- 01
- 013
- 0131
- 01
- 013
- 0132
- 01
- 013
- 0133
I would like the method to group all the levels with an identical code.
And sort the items alphabetically.
Example of what the method should return:
- ROOT
- 01
- 011
- 0111
- 0112
- 0113
- 012
- 0121
- 0122
- 0123
- 013
- 0131
- 0132
- 0133
Example of basic data (Json):
[
{
"id": 0,
"name": "ROOT",
"children": [
{
"name": "01",
"id": 3,
"children": [
{
"name": "013",
"id": 2,
"children": [
{
"name": "0131",
"id": 1
}
]
}
]
},
{
"name": "01",
"id": 6,
"children": [
{
"name": "011",
"id": 5,
"children": [
{
"name": "0112",
"id": 4
}
]
}
]
},
{
"name": "01",
"id": 9,
"children": [
{
"name": "011",
"id": 8,
"children": [
{
"name": "0111",
"id": 7
}
]
}
]
},
{
"name": "01",
"id": 12,
"children": [
{
"name": "013",
"id": 11,
"children": [
{
"name": "0132",
"id": 10
}
]
}
]
},
{
"name": "01",
"id": 15,
"children": [
{
"name": "013",
"id": 14,
"children": [
{
"name": "0133",
"id": 13
}
]
}
]
},
{
"name": "01",
"id": 18,
"children": [
{
"name": "011",
"id": 17,
"children": [
{
"name": "0113",
"id": 16
}
]
}
]
},
{
"name": "01",
"id": 21,
"children": [
{
"name": "012",
"id": 20,
"children": [
{
"name": "0121",
"id": 19,
}
]
}
]
},
{
"name": "01",
"id": 24,
"children": [
{
"name": "012",
"id": 23,
"children": [
{
"name": "0122",
"id": 22
}
]
}
]
},
{
"name": "01",
"id": 27,
"children": [
{
"name": "012",
"id": 26,
"children": [
{
"name": "0123",
"id": 25
}
]
}
]
}
]
}
]
Example of what the method should return (json):
[
{
"id": 0,
"name": "ROOT",
"children": [
{
"name": "01",
"id": 3,
"children": [
{
"name": "011",
"id": 5,
"children": [
{
"name": "0111",
"id": 7
},
{
"name": "0112",
"id": 4
},
{
"name": "0113",
"id": 16
}
]
},
{
"name": "012",
"id": 20,
"children": [
{
"name": "0121",
"id": 19,
},
{
"name": "0122",
"id": 22
},
{
"name": "0123",
"id": 25
}
]
},
{
"name": "013",
"id": 2,
"children": [
{
"name": "0131",
"id": 1
},
{
"name": "0132",
"id": 10
},
{
"name": "0133",
"id": 13
}
]
}
]
},
]
}
]
I think a recursive method would be the most appropriate, but I don't have too many ideas.
The keyword you are looking for might be trie
But without much theory we can just write some code already
The idea is to consider your output as a tree.
Whenever you access a child of your data, you create a node corresponding to that child in the tree if it does not exists yet
Then your recurse, and in-fine you just have to get back your tree-like structure
const data = [{"id":0,"name":"ROOT","children":[{"name":"01","id":3,"children":[{"name":"013","id":2,"children":[{"name":"0131","id":1}]}]},{"name":"01","id":6,"children":[{"name":"011","id":5,"children":[{"name":"0112","id":4}]}]},{"name":"01","id":9,"children":[{"name":"011","id":8,"children":[{"name":"0111","id":7}]}]},{"name":"01","id":12,"children":[{"name":"013","id":11,"children":[{"name":"0132","id":10}]}]},{"name":"01","id":15,"children":[{"name":"013","id":14,"children":[{"name":"0133","id":13}]}]},{"name":"01","id":18,"children":[{"name":"011","id":17,"children":[{"name":"0113","id":16}]}]},{"name":"01","id":21,"children":[{"name":"012","id":20,"children":[{"name":"0121","id":19}]}]},{"name":"01","id":24,"children":[{"name":"012","id":23,"children":[{"name":"0122","id":22}]}]},{"name":"01","id":27,"children":[{"name":"012","id":26,"children":[{"name":"0123","id":25}]}]}]}]
const makeTree = root => {
const toTree = (trieNode, node) => {
trieNode.children = trieNode.children || []
let trieNodeChild = trieNode.children.find(child => child.name === node.name)
if (!trieNodeChild) {
trieNodeChild = { name: node.name, id: node.id }
trieNode.children.push(trieNodeChild)
}
if (!node.children) return
node.children.forEach(child => toTree(trieNodeChild, child))
return trieNode
}
return toTree({}, root)
}
console.time('a')
console.log(JSON.stringify(makeTree(data[0]), null, 2))
console.timeEnd('a')
Note that it is not very efficient because searching in array is slow
A slightly improved version is to store the trieNodes in a map(name => trieNode)
const makeTree2 = root => {
const cache = new Map
const toTree = (trieNode, node) => {
trieNode.children = trieNode.children || []
let trieNodeChild = cache.get(node.name)
if (!trieNodeChild) {
trieNodeChild = { name: node.name, id: node.id }
trieNode.children.push(trieNodeChild)
cache.set(node.name, trieNodeChild)
}
if (!node.children) return
node.children.forEach(child => toTree(trieNodeChild, child))
return trieNode
}
return toTree({}, root)
}
console.time('a2')
console.log(JSON.stringify(makeTree2(data[0]), null, 2))
console.timeEnd('a2')
On my potatoe, first code takes 5ms, second takes 0.3ms. Does not matter much for this data, but order of complexity is apparent.
Here is an interative solution using object-scan
We traverse the tree and remember the nodes that we have seen. If we encounter a node we have seen previously, we merge its children into the previously seen node and delete the node itself.
Note that different ids are picked, since we have to traverse the tree in "delete-safe" order.
// const objectScan = require('object-scan');
const myTree = [{ id: 0, name: 'ROOT', children: [{ name: '01', id: 3, children: [{ name: '013', id: 2, children: [{ name: '0131', id: 1 }] }] }, { name: '01', id: 6, children: [{ name: '011', id: 5, children: [{ name: '0112', id: 4 }] }] }, { name: '01', id: 9, children: [{ name: '011', id: 8, children: [{ name: '0111', id: 7 }] }] }, { name: '01', id: 12, children: [{ name: '013', id: 11, children: [{ name: '0132', id: 10 }] }] }, { name: '01', id: 15, children: [{ name: '013', id: 14, children: [{ name: '0133', id: 13 }] }] }, { name: '01', id: 18, children: [{ name: '011', id: 17, children: [{ name: '0113', id: 16 }] }] }, { name: '01', id: 21, children: [{ name: '012', id: 20, children: [{ name: '0121', id: 19 }] }] }, { name: '01', id: 24, children: [{ name: '012', id: 23, children: [{ name: '0122', id: 22 }] }] }, { name: '01', id: 27, children: [{ name: '012', id: 26, children: [{ name: '0123', id: 25 }] }] }] }];
const rewrite = (tree) => {
const lookup = objectScan(['**(^children$)'], {
useArraySelector: false,
filterFn: ({ parent, property, value, context }) => {
if (value.name in context) {
if (Array.isArray(value.children)) {
if (!Array.isArray(context[value.name].children)) {
context[value.name].children = [];
}
context[value.name].children.push(...value.children);
}
parent.splice(property, 1);
} else {
context[value.name] = value;
}
}
})(tree, {});
const cmpFn = (a, b) => a.name.localeCompare(b.name);
Object.values(lookup)
.filter((v) => Array.isArray(v.children))
.forEach((v) => v.children.sort());
tree.sort(cmpFn);
};
console.log(rewrite(myTree));
// => undefined
console.log(myTree);
// => [ { id: 0, name: 'ROOT', children: [ { name: '01', id: 27, children: [ { name: '012', id: 26, children: [ { name: '0123', id: 25 }, { name: '0122', id: 22 }, { name: '0121', id: 19 } ] }, { name: '011', id: 17, children: [ { name: '0113', id: 16 }, { name: '0111', id: 7 }, { name: '0112', id: 4 } ] }, { name: '013', id: 14, children: [ { name: '0133', id: 13 }, { name: '0132', id: 10 }, { name: '0131', id: 1 } ] } ] } ] } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#16.0.0"></script>
Disclaimer: I'm the author of object-scan

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