Create a nested object with children from array of arrays - javascript

I have the following array of arrays
let arr = [
[ "Female" , "Male" ],
[ "Dinner" , "Lunch" ],
[ "No" , "Yes" ],
]
I'd like to achieve this structure
let foo = [
{
value: "Female",
children: [
{
value: "Dinner",
children: [
{
value: "No"
},
{
value: "Yes"
},
]
},
{
value: "Lunch",
children: [
{
value: "No"
},
{
value: "Yes"
},
]
},
]
},
{
value: "Male",
children: [
{
value: "Dinner",
children: [
{
value: "No"
},
{
value: "Yes"
},
]
},
{
value: "Lunch",
children: [
{
value: "No"
},
{
value: "Yes"
},
]
},
]
},
]
I simply can't wrap my head around the problem to achieve this, thus, I don't have a starting code to post, so please if you can help, it would be great.

recursion
Recursion is a functional heritage and so using it with functional style yields the best results. This means avoiding things like mutation, variable reassignments, and other side effects.
We can write make(t) using inductive inductive reasoning -
If the input t is empty, return the empty result []
(inductive) t has at least one element. For all value in the first element t[0], return a new object {value, children} where children is the result of the recursive sub-problem make(t.slice(1))
const make = t =>
t.length == 0
? [] // 1
: t[0].map(value => ({ value, children: make(t.slice(1)) })) // 2
const myinput = [
[ "Female" , "Male" ],
[ "Dinner" , "Lunch" ],
[ "No" , "Yes" ]
]
console.log(make(myinput))
Above we write make as a single pure functional expression using ?:. This is equivalent to an imperative style if..else -
function make(t) {
if (t.length == 0)
return []
else
return t[0].map(value => ({ value, children: make(t.slice(1)) }))
}
const myinput = [
[ "Female" , "Male" ],
[ "Dinner" , "Lunch" ],
[ "No" , "Yes" ]
]
console.log(make(myinput))
visualize
It helps for us to visualize how these work
make([[ "Female" , "Male" ], [ "Dinner" , "Lunch" ], [ "No" , "Yes" ]])
= [
{value: "Female", children: make([[ "Dinner" , "Lunch" ], [ "No" , "Yes" ]]) },
{value: "Male", children: make([[ "Dinner" , "Lunch" ], [ "No" , "Yes" ]]) }
]
make([[ "Dinner" , "Lunch" ], [ "No" , "Yes" ]])
= [
{value: "Dinner", children: make([[ "No" , "Yes" ]]) },
{value: "Lunch", children: make([[ "No" , "Yes" ]]) }
}
make([[ "No" , "Yes" ]])
= [
{value: "No", children: make([]) },
{value: "Yes", children: make([]) }
}
make([])
= []
remove empty children
Now that we see how it works, we prevent making empty children: [] properties by adding one more conditional. When t has just one element, simply create a {value} for all value in the element -
function make(t) {
switch (t.length) {
case 0:
return []
case 1:
return t[0].map(value => ({ value }))
default:
return t[0].map(value => ({ value, children: make(t.slice(1)) }))
}
}
const myinput = [
[ "Female" , "Male" ],
[ "Dinner" , "Lunch" ],
[ "No" , "Yes" ]
]
console.log(make(myinput))
Which produces the output you are looking for -
[
{
"value": "Female",
"children": [
{
"value": "Dinner",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
},
{
"value": "Lunch",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
}
]
},
{
"value": "Male",
"children": [
{
"value": "Dinner",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
},
{
"value": "Lunch",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
}
]
}
]

You can also do it without recursion with 2 for
let arr = [
[ "Female" , "Male" ],
[ "Dinner" , "Lunch" ],
[ "No" , "Yes" ],
];
var lastChild = -1;
for(var i = arr.length-1; i >= 0; i--) {
var item = arr[i];
var lastChildTemp = [];
for(var j = 0; j < item.length; j++) {
var newChild = {value: item[j]};
if(lastChild != -1) {
newChild.children = lastChild;
}
lastChildTemp.push(newChild);
}
lastChild = lastChildTemp;
}
console.log(JSON.stringify(lastChildTemp,null,2));
Output:
[
{
"value": "Female",
"children": [
{
"value": "Dinner",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
},
{
"value": "Lunch",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
}
]
},
{
"value": "Male",
"children": [
{
"value": "Dinner",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
},
{
"value": "Lunch",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
}
]
}
]
The key here is to use backward for (starting from high index to low index), then create a lastChild object. Then put it in .children attribute of each next objects.

Rearrange your Array using the below code, then iterate as your wish and this is dynamic. you can have more rows in arr variable.
let arr = [
[ "Female" , "Male" ],
[ "Dinner" , "Lunch" ],
[ "No" , "Yes" ],
]
for(let i=arr.length-2; i>-1; i--){
for(let j=0; j< arr[i].length; j++) {
item = {}
item[arr[i][j]] = arr[i+1];
arr[i][j] = [];
arr[i][j] = item;
}
arr.pop();
}
console.log(arr);
/*output*/
[
[{
'Female': [{
'Dinner': ['No', 'Yes']
}, {
'Lunch': ['No', 'Yes']
}]
}, {
'Male': [{
'Dinner': ['No', 'Yes']
}, {
'Lunch': ['No', 'Yes']
}]
}]
]
https://jsfiddle.net/Frangly/ywsL0pbt/149/

You can try this:
let arr = [
['Female', 'Male'],
['Dinner', 'Lunch'],
['No', 'Yes']
]
function makeTree(a, ch = [], currIndex = 0) {
for (const item of a[currIndex]) {
if (a[currIndex + 1]) {
// If there is an array after this one then
// include the 'children' array
const obj = { value: item, children: [] }
ch.push(obj)
// Run the function again to fill the `children`
// array with the values of the next array
makeTree(a, obj.children, currIndex + 1)
} else {
// If this is the last array then
// just include the value
ch.push({ value: item })
}
}
return ch
}
const result = makeTree(arr)
console.log(JSON.stringify(result, null, 2))
.as-console-wrapper { min-height: 100% }

Checkout this code snippet. It outputs as per your need.
let arr = [
[ "Female" , "Male" ],
[ "Dinner" , "Lunch" ],
[ "No" , "Yes" ],
]
let foo = [];
let arr2 = [];
arr[2].forEach(yn => {
arr2.push({ "value": yn});
});
let arr1 = [];
arr[1].forEach(dl => {
arr1.push({
"value": dl,
"children": arr2
});
});
arr[0].forEach(fm => {
foo.push({
"value": fm,
"children": arr1
});
});
console.log(JSON.stringify(foo, null, 2))

Related

Map the values of a object to the values of another object

I have a JSON array
{
"data": [
{
"id": 659,
"source_id": 1,
"created_at": "2023-01-13T06:35:08.000000Z",
"products": [
{
"name": "532",
"properties": [
{
"name": "color",
"value": "blue"
},
{
"name": "size",
"value": "1"
}
],
}
]
},
{
"id": 658,
"source_id": 2,
"created_at": "2023-01-12T21:36:06.000000Z",
"products": [
{
"name": "532",
"properties": [
{
"name": "color",
"value": "khaki"
},
{
"name": "size",
"value": "2"
}
],
}
]
},
},
],
}
All code I have so far:
var rows = [], sortOrder = ['fabric', 'color', 'size'], orderSource = [{"Insta" : 1, "Retail" : 2}];
dataSet.forEach((e) => {
e.products.forEach((product) => {
product.properties = product.properties.sort((a, b) => {
return sortOrder.indexOf(a.name) - sortOrder.indexOf(b.name);
});
product.properties = sortOrder.map((i => name => product.properties[i].name === name ?
product.properties[i++] : {name, value : ''})(0));
rows.push([e.id, e.source_id, new Date(e.created_at).toLocaleDateString('uk-UK'),
product.name].concat(product.properties.map(p => p.value)).concat([product.quantity,
product.comment]));
console.log(rows);
});
});
Console output looks like this
[ 659, 1, '13.01.2023', '532', 'blue', '1' ],
[ 658, 2, '12.01.2023', '532', 'khaki', '2' ]
I need the data from the array orderSource = [{"Insta" : 1, "Retail" : 2}] to be reassigned to the resulting array so that it looks like this
[ 659, 'Insta', '13.01.2023', '532', 'blue', '1' ],
[ 658, 'Retail', '12.01.2023', '532', 'khaki', '2' ]
This is necessary in order to then write the array to the Google spreadsheet
I am new to programming, so I do not consider it necessary to list here all my attempts to reassign values in the resulting array))
Any of your help is welcome
Use the array method Array.find() on the object's keys to find the relevant key (e.g. Insta, Retail).
const rows = [],
sortOrder = ['fabric', 'color', 'size'],
orderSource = [{
"Insta": 1,
"Retail": 2
}];
dataSet.forEach((e) => {
e.products.forEach((product) => {
product.properties = product.properties.sort((a, b) => {
return sortOrder.indexOf(a.name) - sortOrder.indexOf(b.name);
});
product.properties = sortOrder.map((i => name => product.properties[i].name === name ?
product.properties[i++] : {
name,
value: ''
})(0));
// Use the Array.find() method on the object's keys
const key = Object.keys(orderSource[0]).find(key => orderSource[0][key] === e.source_id);
rows.push([e.id, key, new Date(e.created_at).toLocaleDateString('uk-UK'),
product.name
].concat(product.properties.map(p => p.value)).concat([product.quantity,
product.comment
]));
});
});
console.log(rows);

Form a nested tree from an array of objects in javascript

So there is array of objects of below format
let inputs = [
{
"id": "614344d9d9c21c0001e6af2e",
"groupName": "Unassigned",
"parentGroup": "null"
},
{
"id": "614447da152f69c3c1d52f2e",
"groupName": "P1",
"parentGroup": "null"
},
{
"id": "614447da152f69c3c1d52f38",
"groupName": "K1",
"parentGroup": "C1"
},
{
"id": "614447da152f69c3c1d52f3e",
"groupName": "A2",
"parentGroup": "C2"
},
{
"id": "614447da152f69c3c1d52f40",
"groupName": "G1",
"parentGroup": "P2"
},
{
"id": "614447da152f69c3c1d52f46",
"groupName": "F1",
"parentGroup": "null"
},
{
"id": "614447da152f69c3c1d52f30",
"groupName": "P2",
"parentGroup": "null"
},
{
"id": "614447da152f69c3c1d52f36",
"groupName": "C2",
"parentGroup": "P1"
},
{
"id": "614447da152f69c3c1d52f3c",
"groupName": "A1",
"parentGroup": "C2"
},
{
"id": "614447da152f69c3c1d52f34",
"groupName": "C1",
"parentGroup": "P1"
},
{
"id": "614447da152f69c3c1d52f32",
"groupName": "P3",
"parentGroup": "null"
},
{
"id": "614447da152f69c3c1d52f3a",
"groupName": "K2",
"parentGroup": "C1"
},
{
"id": "614447da152f69c3c1d52f42",
"groupName": "GG1",
"parentGroup": "G1"
},
{
"id": "614447da152f69c3c1d52f44",
"groupName": "GGG1",
"parentGroup": "GG1"
}
]
i am trying to create a tree structure of format
{name:'p1',children:[{name:'c1':children:[]}]}
so i sorted all the elements of given array considering element with parentGroup as "null" to be at the top of the array.
let finalArr = [];
inputs.sort((a,b)=> (a.parentGroup === "null") ? -1 : 1);
And for each element of the inputs array, i was iterating below logic
inputs.forEach(ele => {
if(ele.parentGroup === "null"){
let child= {name:ele.groupName,children:[]};
finalArr.push(child);
}else{
finalArr.forEach(item => {
this.findNode(item,ele);
})
}
});
If the 'parentGroup' of element is "null", then create a leaf kind of obj and push the element to 'finalArr' array
Else, then iterate across all the elements of 'finalArr' over a recursion function
public findNode(ele, obj){
if(ele.children.length === 0){
if(ele.name === obj.parentGroup){
let child = {name:obj.groupName, children:[]};
ele.children.push(child);
}
}else{
let j = ele.children.length-1;
this.findNode(ele.children[j--],obj);
}
}
This recursion function will check the element has children or not, if no children, then compare the parentGroup of given obj, with name of element from 'FinalArr'.
if so ,push the current obj to the children of the element of finalArr.
else, that is, when children has more elements, the same recursion will be triggered until depth of the element is reached.
With this i tought i would make a tree structure with given inputs array, but when a parent has more children, of same level, this logic fails,
so the inputs array has 'c1' which is a child of 'p1', but nly the child 'c2' resides, not sure the what is that i missed.
You could take a standard algorithm for getting a tree with given data
const
getTree = (data, id, parent, root, fn = o => o) => {
var t = {};
data.forEach(o => ((t[o[parent]] ??= {}).children ??= []).push(Object.assign(t[o[id]] = t[o[id]] || {}, fn(o))));
return t[root].children;
},
data = [{ id: "614344d9d9c21c0001e6af2e", groupName: "Unassigned", parentGroup: "null" }, { id: "614447da152f69c3c1d52f2e", groupName: "P1", parentGroup: "null" }, { id: "614447da152f69c3c1d52f38", groupName: "K1", parentGroup: "C1" }, { id: "614447da152f69c3c1d52f3e", groupName: "A2", parentGroup: "C2" }, { id: "614447da152f69c3c1d52f40", groupName: "G1", parentGroup: "P2" }, { id: "614447da152f69c3c1d52f46", groupName: "F1", parentGroup: "null" }, { id: "614447da152f69c3c1d52f30", groupName: "P2", parentGroup: "null" }, { id: "614447da152f69c3c1d52f36", groupName: "C2", parentGroup: "P1" }, { id: "614447da152f69c3c1d52f3c", groupName: "A1", parentGroup: "C2" }, { id: "614447da152f69c3c1d52f34", groupName: "C1", parentGroup: "P1" }, { id: "614447da152f69c3c1d52f32", groupName: "P3", parentGroup: "null" }, { id: "614447da152f69c3c1d52f3a", groupName: "K2", parentGroup: "C1" }, { id: "614447da152f69c3c1d52f42", groupName: "GG1", parentGroup: "G1" }, { id: "614447da152f69c3c1d52f44", groupName: "GGG1", parentGroup: "GG1" }],
tree = getTree(data, 'groupName', 'parentGroup', null, ({ groupName: name }) => ({ name }));
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I think the issue is how finalArr is used to generate the html elements.
When doing console.log(finalArr) it looks like the code block below. So it seems to me like the code you have to build the structure of finalArr is working fine.
// console.log(finalArr)
[
{ "name": "P3", "children": [] },
{
"name": "P2",
"children": [
{
"name": "G1",
"children": [
{ "name": "GG1", "children": [
{ "name": "GGG1", "children": [] }
]
}
]
}
]
},
{ "name": "F1", "children": [] },
{
"name": "P1",
"children": [
{ "name": "C2", "children": [
{ "name": "A1", "children": [] }
]
}
]
},
{ "name": "Unassigned", "children": [] }
]
EDIT
As OP mentioned in the comment C1 was missing. I've introduced a root element that will have the finalArr as its children and changed the findNode to use a for loop instead of forEach. In this way we can also break when we find the node and not continue recursing.
As part of the initial sorting we will sort the inputs by parentGroup so we ensure that a childs parent is added in the tree structure before we try to find it with findNode.
I believe this produces the correct result:
inputs.sort((a, b) => (a.parentGroup === "null" ? -1 : 1));
// Sort by parentGroup
let root = inputs.pop();
let inputsDescending = [root];
let max = inputs.length * inputs.length;
let c = 0;
while (inputs.length > 0 && max > c) {
const child = inputs.pop();
const hasParentGroup = inputsDescending.find(
(parent) => parent.groupName === child.parentGroup
);
if (hasParentGroup || child.parentGroup === "null") {
inputsDescending.push(child);
} else {
inputs.unshift(child);
}
}
let rootEle = { name: "root", children: [] };
inputsDescending.forEach((obj) => {
if (obj.parentGroup === "null") {
let child = { name: obj.groupName, children: [] };
rootEle.children.push(child);
} else {
findNode(rootEle, obj);
}
});
function findNode(ele, obj) {
if (ele.name === obj.parentGroup) {
let child = { name: obj.groupName, children: [] };
ele.children.push(child);
return true;
} else {
const c = ele.children.length;
if (c > 0) {
for (let i = 0; c > i; i++) {
const found = findNode(ele.children[i], obj);
if (found) break;
}
}
}
}
const finalArr = rootEle.children;
Now finalArr looks like this:
[
{ "name": "Unassigned", "children": [] },
{
"name": "P1",
"children": [
{
"name": "C1",
"children": [
{ "name": "K1", "children": [] },
{ "name": "K2", "children": [] }
]
},
{
"name": "C2",
"children": [
{ "name": "A2", "children": [] },
{ "name": "A1", "children": [] }
]
}
]
},
{ "name": "F1", "children": [] },
{
"name": "P2",
"children": [
{ "name": "G1", "children": [
{ "name": "GG1", "children": [] }
]
}
]
},
{ "name": "P3", "children": [] }
]

How to group array objects into sub objects and count them

I receive an array from an api which looks like this
input = [
{ choices:[
{"food":"breakfast","preference":"tea"},
{"food":"lunch","preference":"burger"},
{"food":"supper","preference":"rice"}
]
},
{ choices:[
{"food":"breakfast","preference":"coffee"},
{"food":"lunch","preference":"burger"},
{"food":"supper","preference":"yam"}
]
},
{ choices:[
{"food":"breakfast","preference":"tea"},
{"food":"lunch","preference":"bread"},
{"food":"supper","preference":"yam"}
]
},
{ choices:[
{"food":"breakfast","preference":"coffee"},
{"food":"lunch","preference":"bread"},
{"food":"supper","preference":"rice"}
]
}
]
I tried Group by the array of objects based on the property and also the count in javascript
I need to group the individual preferences and count them
groupedChoices = [
[
{ "preference": "tea", "count": 3 },
{ "preference": "coffee", "count": 2 }
],
[
{ "preference": "burger", "count": 3 },
{ "preference": "bread", "count": 2 }
],
[
{ "preference": "rice", "count": 3 },
{ "preference": "yam", "count": 2 }
]
]
const input = [
{ choices:[
{"food":"breakfast","preference":"tea"},
{"food":"lunch","preference":"burger"},
{"food":"supper","preference":"rice"}
]
},
{ choices:[
{"food":"breakfast","preference":"coffee"},
{"food":"lunch","preference":"burger"},
{"food":"supper","preference":"yam"}
]
},
{choices: [
{"food":"breakfast","preference":"tea"},
{"food":"lunch","preference":"bread"},
{"food":"supper","preference":"yam"}
]
},
{ choices:[
{"food":"breakfast","preference":"coffee"},
{"food":"lunch","preference":"bread"},
{"food":"supper","preference":"rice"}
]
}
];
const result = input.reduce((acc, { choices }) => {
choices.forEach(({ food, preference }) => {
if (!acc[food]) acc[food] = {}
if (!acc[food][preference]) acc[food][preference] = 1
else ++acc[food][preference]
})
return acc
}, {})
console.log(result)

Javascript merge array of objects based on incrementing key

This is what I have: I want to merge object which key begins with "path-"+i . And to strip "path-i" from keys in end result.
var arr = [
{
"key": "path-0-mp4",
"value": [
"media/video/01.mp4",
"media/video/01_hd.mp4"
]
},
{
"key": "path-0-quality",
"value": [
"720p",
"1080p"
]
},
{
"key": "path-1-mp4",
"value": [
"media/video/02.mp4",
"media/video/02_hd.mp4"
]
},
{
"key": "path-1-quality",
"value": [
"SD",
"HD"
]
}
]
This is a desired result:
var arr = [
[
{
"mp4": "media/video/01.mp4",
"quality": "720p"
},
{
"mp4": "media/video/01_hd.mp4",
"quality": "1080p"
},
],
[
{
"mp4": "media/video/02.mp4",
"quality": "SD"
},
{
"mp4": "media/video/02_hd.mp4",
"quality": "HD"
},
],
]
I started doing something but its not even close:
var key, new_key, value,j=0, z=0, parr = [], obj;
for(var i = 0;i<a.length;i++){
console.log('item:' ,a[i])
key = a[i].key, value = a[i].value
if(key.indexOf('path-'+j.toString()) > -1){
new_key = key.substr(key.lastIndexOf('-')+1)
console.log(key, new_key, value)
for(var z = 0;z<value.length;z++){
parr.push({[new_key]: value[z] })
}
}
}
console.log(parr)
[
{
"mp4": "media/video/01.mp4"
},
{
"mp4": "media/video/01_hd.mp4"
},
{
"quality": "720p"
},
{
"quality": "1080p"
}
]
edit:
Array could petencially hols different keys that would need grouping in the same way, for example:
var arr = [
{
"key": "path-0-mp4",
"value": [
"media/video/01.mp4",
"media/video/01_hd.mp4"
]
},
{
"key": "path-0-quality",
"value": [
"720p",
"1080p"
]
},
{
"key": "path-1-mp4",
"value": [
"media/video/02.mp4",
"media/video/02_hd.mp4"
]
},
{
"key": "path-1-quality",
"value": [
"SD",
"HD"
]
},
{
"key": "subtitle-0-label",
"value": [
"English",
"German",
"Spanish"
]
},
{
"key": "subtitle-0-src",
"value": [
"data/subtitles/sintel-en.vtt",
"data/subtitles/sintel-de.vtt",
"data/subtitles/sintel-es.vtt"
]
},
{
"key": "subtitle-1-label",
"value": [
"German",
"Spanish"
]
},
{
"key": "subtitle-1-src",
"value": [
"data/subtitles/tumblr-de.vtt",
"data/subtitles/tumblr-es.vtt"
]
}
]
This is a desired result (create new array for each different key):
var arr = [
[
{
"mp4": "media/video/01.mp4",
"quality": "720p"
},
{
"mp4": "media/video/01_hd.mp4",
"quality": "1080p"
},
],
[
{
"mp4": "media/video/02.mp4",
"quality": "SD"
},
{
"mp4": "media/video/02_hd.mp4",
"quality": "HD"
},
],
],
arr2 = [
[
{
"label": "English",
"src": "data/subtitles/sintel-en.vtt",
},
{
"label": "German",
"src": "data/subtitles/sintel-de.vtt"
},
{
"label": "Spanish",
"src": "data/subtitles/sintel-es.vtt"
}
],
[
{
"label": "Spanish",
"src": "data/subtitles/tumblr-es.vtt",
},
{
"label": "German",
"src": "data/subtitles/tumblr-de.vtt"
}
]
]
You could split the key property, omit the first path and take the rest as index and key. Then create a new array, if not exists and assign the values.
var data = [{ key: "path-0-mp4", value: ["media/video/01.mp4", "media/video/01_hd.mp4"] }, { key: "path-0-quality", value: ["720p", "1080p"] }, { key: "path-1-mp4", value: ["media/video/02.mp4", "media/video/02_hd.mp4"] }, { key: "path-1-quality", value: ["SD", "HD"] }],
result = data.reduce((r, { key, value }) => {
let [, i, k] = key.split('-');
r[i] = r[i] || [];
value.forEach((v, j) => (r[i][j] = r[i][j] || {})[k] = v);
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you like to group by the first part of key, you could take an object with this group as key and assign the rest as above.
var data = [{ key: "path-0-mp4", value: ["media/video/01.mp4", "media/video/01_hd.mp4"] }, { key: "path-0-quality", value: ["720p", "1080p"] }, { key: "path-1-mp4", value: ["media/video/02.mp4", "media/video/02_hd.mp4"] }, { key: "path-1-quality", value: ["SD", "HD"] }, { key: "subtitle-0-label", value: ["English", "German", "Spanish"] }, { key: "subtitle-0-src", value: ["data/subtitles/sintel-en.vtt", "data/subtitles/sintel-de.vtt", "data/subtitles/sintel-es.vtt"] }, { key: "subtitle-1-label", value: ["German", "Spanish"] }, { key: "subtitle-1-src", value: ["data/subtitles/tumblr-de.vtt", "data/subtitles/tumblr-es.vtt"] }],
result = data.reduce((r, { key, value }) => {
let [group, i, k] = key.split('-');
if (!r[group]) r[group] = [];
if (!r[group][i]) r[group][i] = [];
value.forEach((v, j) => {
if (!r[group][i][j]) r[group][i][j] = {};
r[group][i][j][k] = v;
});
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I am new to this and a beginner,
is this the correct approach?
const a = [{ "key": "path-0-mp4", "value": [ "media/video/01.mp4", "media/video/01_hd.mp4" ] }, { "key": "path-0-quality", "value": [ "720p", "1080p" ] }, { "key": "path-1-mp4", "value": [ "media/video/02.mp4", "media/video/02_hd.mp4" ] }, { "key": "path-1-quality", "value": [ "SD", "HD" ] } ];
var resp = [];
for (let i = 0; i < a.length; i++) {
var inst = a[i];
var key = inst["key"];
for (let j = 0; j < inst.value.length; j++) {
var index = key.split("-")[1];
var keyinst = key.split("-")[2];
if (!resp[index]) {
resp[index] = [];
}
if (!resp[index][j]) {
resp[index][j] = {};
}
resp[index][j][keyinst] = inst.value[j];
}
}
console.log(resp);
I find this easier to read and grasp
You can save an assignment if you use reduce
const arr = [{ "key": "path-0-mp4", "value": [ "media/video/01.mp4", "media/video/01_hd.mp4" ] }, { "key": "path-0-quality", "value": [ "720p", "1080p" ] }, { "key": "path-1-mp4", "value": [ "media/video/02.mp4", "media/video/02_hd.mp4" ] }, { "key": "path-1-quality", "value": [ "SD", "HD" ] } ];
newArr = [];
arr.filter(item => item.key.endsWith("mp4"))
.forEach(item => item.value
.forEach((val, i) => newArr.push({
"mp4": val,
"quality": arr.find(qItem => qItem.key === item.key.replace("mp4", "quality")).value[i]}
)
)
)
console.log(newArr)
Here is Nina's version in an unobfuscated version
var data = [{ key: "path-0-mp4", value: ["media/video/01.mp4", "media/video/01_hd.mp4"] }, { key: "path-0-quality", value: ["720p", "1080p"] }, { key: "path-1-mp4", value: ["media/video/02.mp4", "media/video/02_hd.mp4"] }, { key: "path-1-quality", value: ["SD", "HD"] }],
result = data.reduce((resultArray, { key, value }) => {
let [, idx, suffix] = key.split('-');
resultArray[idx] = resultArray[idx] || [];
value.forEach((val, i) => (resultArray[idx][i] = resultArray[idx][i] || {})[suffix] = val);
return resultArray;
}, []);
console.log(result);
The only odd thing I did here was using an object as a lookup table to help with the speed complexity. If you have any questions let me know.
const arr = [{ "key": "path-0-mp4", "value": [ "media/video/01.mp4", "media/video/01_hd.mp4" ] }, { "key": "path-0-quality", "value": [ "720p", "1080p" ] }, { "key": "path-1-mp4", "value": [ "media/video/02.mp4", "media/video/02_hd.mp4" ] }, { "key": "path-1-quality", "value": [ "SD", "HD" ] } ];
const result = arr.reduce((table, item) => {
// Getting "path-1" from "path-1-quality"
const pathValues = item.key.split('-');
const pathValue = pathValues[0] + '-' + pathValues[1];
// Getting "quality" from "path-1-quality"
const key = pathValues[2];
// Get Index from table if already registered paths
let tIndex = table.indexLookup[pathValue];
// If there is no registered index register one
if (tIndex === undefined) {
// reassign index to new location
tIndex = table.result.length;
// register the index
table.indexLookup[pathValue] = tIndex;
table.result.push([]);
}
// Assign values
item.value.forEach((value, i) => {
const arr = table.result[tIndex] || [];
arr[i] = arr[i] || {}
arr[i][key] = value;
table.result[tIndex] = arr;
})
return table
}, {
indexLookup : {},
result: []
}).result
console.log(result)

Group objects by multiple values and combining duplicates

I have an array of data that looks like this:
{
"data": [
{
"id": "20200722_3",
"eventDate": "2020-07-22T00:00:00",
"eventName": "Football",
"eventDetails": [
"Men's First Round (2 matches)"
],
"eventVenue": "Venue A"
},
{
"id": "20200722_1",
"eventDate": "2020-07-22T00:00:00",
"eventName": "Football",
"eventDetails": [
"Men's First Round (2 matches)"
],
"eventVenue": "Venue B"
}
]
}
Now I wanted to group the data by multiple properties. For example, by eventDate, eventName, eventDetails, and eventVenue. Which I've done with this code referenced from this post:
const groupBy = (array, attrs) => {
var output = [];
for (var i = 0; i < array.length; ++i) {
var ele = array[i];
var groups = output;
for (var j = 0; j < attrs.length; ++j) {
var attr = attrs[j];
var value = ele[attr];
var gs = groups.filter(g => {
return g.hasOwnProperty('label') && g['label'] === value;
});
if (gs.length === 0) {
var g = {};
if (isArray.g['label'] ) {
}
g['label'] = value;
g['groups'] = [];
groups.push(g);
groups = g['groups'];
} else {
groups = gs[0]['groups'];
}
}
groups.push(ele);
}
return output;
}
var result = groupBy(data, ['eventDate', 'eventName', 'eventDetails', 'eventVenue'])
Which results in an array like this:
[{
"label": "2020-07-23T00:00:00",
"groups": [{
"label": "Football",
"groups": [{
"label": [
"Men's First Round (2 matches)"
],
"groups": [{
"label": "Venue A",
"groups": [
"Object"
]
}]
},
{
"label": [
"Men's First Round (2 matches)"
],
"groups": [{
"label": "Venue B",
"groups": [
"Object"
]
}]
}
}]
}]
}]
You can see that for the output above, there are two separate "groups" that have the label "Men's First Round (2 matches)". I'm trying to figure out how I can combine these objects that have duplicate value ? I'm looking for something that would output like this:
[{
"label": "2020-07-23T00:00:00",
"groups": [{
"label": "Football",
"groups": [{
"label": [
"Men's First Round (2 matches)"
],
"groups": [{
"label": "Venue A",
"groups": [
"Object"
]
},
{
"label": "Venue B",
"groups": [
"Object"
]
}]
}]
}]
}]
Any help would be greatly appreciated.
I'll share the answer that I came up with for those that are curious.
For my needs, I know that if the array with the eventName only contained 1 attribute, it could be a duplicate. So in order to fix that, I converted the array that only had 1 value to a string:
if (ele[attr] && ele[attr].length === 1) {
var value = ele[attr].toString();
} else {
var value = ele[attr];
}

Categories