Transforming an array into an array of arrays - javascript

I am trying to do an opposite of flattening an array.
I have the following input JSON array of 4 elements:
[
{
"nestedObj": {
"id":12
}
},
{
"nestedObj": {
"id":555
}
},
{
"nestedObj": {
"id":555
}
},
{
"nestedObj" :{
"id":771
}
}
]
I want to transform it to an array of arrays, where each subarray has elements of the same nestedObj.id grouped up together.
I can assume the initial JSON is sorted by nestedObj.id.
In the above example, the id of nestedObj of 2nd and 3rd element are the same (555), so those elements would be grouped into one sub-array.
This would be the result, an array of only 3 sub-array elements:
[
[{
"nestedObj": {
"id":12
}
}],
[{
"nestedObj": {
"id":555
}
},
{
"nestedObj": {
"id":555
}
}],
[{
"nestedObj" :{
"id":771
}
}]
]
And this is the code that gets me what I want:
const data = [ /* ...the above input data... */ ];
let result = [];
let prevId = null;
for (let elem of data) {
let currId = elem.nestedObj.id;
if (currId === prevId) {
result[result.length - 1].push({...elem});
} else {
result.push([{...elem}]);
}
prevId = currId;
}
But as you can see... the code is very declarative. It's not very JavaScript-like, in a functional programming sense.
How can I re-write it using e.g. reduce or other 'modern JS' techniques?

Just group the objects.
let array = [{ nestedObj: { id: 12 } }, { nestedObj: { id: 555 } }, { nestedObj: { id: 555 } }, { nestedObj: { id: 771 } }],
result = Object.values(array.reduce((r, o) => {
(r[o.nestedObj.id] = r[o.nestedObj.id] || []).push(o);
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You can group by id using the function reduce, extract the grouped values using the function Object.values, and finally map the array to build the desired output.
This is assuming we have only one attribute called nestedObj
let arr = [{ nestedObj: { id: 12 } }, { nestedObj: { id: 555 } }, { nestedObj: { id: 555 } }, { nestedObj: { id: 771 } }],
result = Object.values(arr.reduce((a, {nestedObj: {id}}) => {
(a[id] || (a[id] = [])).push(id);
return a;
}, {})).map(r => r.map(id => ({nestedObj: {id}})));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Use a Map to group the items with same id then get the final values from the Map
const data = [{ nestedObj: { id: 12 } }, { nestedObj: { id: 555 } }, { nestedObj: { id: 555 } }, { nestedObj: { id: 771 } }]
const map = new Map;
data.forEach(o => {
const {nestedObj:{id}} = o;
map.has(id) ? map.get(id).push(o) : map.set(id,[o]);
});
console.log([...map.values()])

Related

Convert flat structure with any number of levels array into tree structure

I want to convert flat structure array into a tree structure for one my projects. Below is the input and expected output:
Input:
let input=[
{
lvl1:"Code1",
lvl2:"Type1",
lvl3:"Desc1",
lvl4:"Check1"
},
{
lvl1:"Code1",
lvl2:"Type1",
lvl3:"Desc1",
lvl4:"Check2"
},
{
lvl1:"Code2",
lvl2:"Type2",
lvl3:"Desc2",
lvl4:"Check1"
},
]
Output:
[
{
level_key:"lvl1",
level_value:"Code1",
children:[
{
level_key:"lvl2",
level_value:"Type1",
children:[
{
level_key:"lvl3",
level_value:"Desc1",
children:[
{
level_key:"lvl4",
level_value:"Check1",
children:[]
},
{
level_key:"lvl4",
level_value:"Check2",
children:[]
}
]
}
]
}
]
},
{
level_key:"lvl1",
level_value:"Code2",
children:[
{
level_key:"lvl2",
level_value:"Type2",
children:[
{
level_key:"lvl3",
level_value:"Desc2",
children:[
{
level_key:"lvl4",
level_value:"Check1",
children:[]
}
]
}
]
}
]
}
]
Here in eg. i have taken till lvl4 but any number of levels could be there like lvl5, lvl6....
I have tried a approach but i feel that is very complex and not scalable.
You could take obbjects with level_value as key and take the arrays as result.
const
flat = [{ lvl1:"Code1", lvl2:"Type1", lvl3:"Desc1", lvl4:"Check1" }, { lvl1:"Code1", lvl2:"Type1", lvl3:"Desc1", lvl4:"Check2" }, { lvl1:"Code2", lvl2:"Type2", lvl3:"Desc2", lvl4:"Check1" }],
tree = flat.reduce((r, o) => {
let temp = r,
i = 1,
level_key = `lvl${i}`,
level_value = o[level_key];
do {
if (!temp[level_value]) {
temp[level_value] = { _: [] };
temp._.push({ level_key, level_value, children: temp[level_value]._ });
}
temp = temp[level_value];
level_key = `lvl${++i}`;
level_value = o[level_key];
} while (level_value)
return r;
}, { _: [] })._;
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Modify array of the nested objects by its nested object's properties

I have the following structure in my array.
const data = [
{
name: 'Book_1',
value: {
eng: 'english_book_1',
sp: 'spanish_book_1'
}
},
{
name: 'Book_2',
value: {
eng: 'english_book_2',
sp: 'spanish_book_2'
}
}
];
And trying to get a structure like this:
[
{
eng: {
Book_1: 'english_book_1',
Book_2: 'english_book_2'
}
},
{
sp: {
Book_1: 'spanish_book_1',
Book_2: 'spanish_book_2'
}
}
];
So, the array should have language keys and nested book names with values in it.
I tried a couple of things but even close.
const modified = [];
const object = {};
data.forEach(el => {
Object.entries(el.value).forEach(([name, value]) => {
if (object['name']) {
}
modified.push(object);
});
});
```
Thanks.
You could reduce the array by taking an object for the language's objects.
const
data = [{ name: 'Book_1', value: { eng: 'english_book_1', sp: 'spanish_book_1' } }, { name: 'Book_2', value: { eng: 'english_book_2', sp: 'spanish_book_2' } }],
result = Object.values(data.reduce((r, { name, value }) => {
Object
.entries(value)
.forEach(([k, v]) => (r[k] ??= { [k]: {} })[k][name] = v);
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

convert a flat array that has a "path" property to a nested array

I have a flat array like this example :
[
{
'name':'itemA',
'path':'foo/bar'
},
{
'name':'itemB',
'path':'bar/foo'
},
{
'name':'itemC',
'path':'foo'
},
{
'name':'itemD',
'path':'bar'
},
{
'name':'itemE',
'path':'foo/bar/wizz'
},
{
'name':'itemF',
'path':'bar/foo'
},
]
I want to build a tree based on the "path" property, so I could get this output :
[
{
'name':'itemD',
'path':'bar',
'items':[
{
'name':'itemD',
'path':'bar/foo'
},
{
'name':'itemF',
'path':'bar/foo'
}
]
},
{
'name':'itemC',
'path':'foo',
'items':[
{
'name':'itemA',
'path':'foo/bar',
'items':
[
{
'name':'itemE',
'path':'foo/bar/wizz'
}
]
},
]
}
]
How could I achieve that ?
I found out some examples like this one, but they are based on a parent ID and not a "path" like mine.
Thanks a lot !
You could find the level or add a new object for the level of the splitted path.
const
data = [{ name: 'itemA', path: 'foo/bar' }, { name: 'itemB', path: 'bar/foo' }, { name: 'itemC', path: 'foo' }, { name: 'itemD', path: 'bar' }, { name: 'itemE', path: 'foo/bar/wizz' }, { name: 'itemF', path: 'bar/foo' }],
tree = data.reduce((items, { name, path }) => {
path.split('/').reduce((o, _, i, p) => {
let path = p.slice(0, i + 1).join('/'),
temp = (o.items ??= []).find(q => q.path === path);
if (!temp) o.items.push(temp = { name, path });
return temp;
}, { items });
return items;
}, []);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Relate and merge array of same Department

I am working on an application where I need to get combine the object of same department based on the
conditions provided in the second Array and attach the relation to the object.
let inArr1 = [{"D1D2":"AND"},{"D3D4":"OR"}]
let inArr2 =[{"ID":"1","NAME":"KEN","DEPT1":"CSE"},
{"ID":"2","NAME":"MARK","DEPT2":"IT"},
{"ID":"3","NAME":"TOM","DEPT3":"ECE"},
{"ID":"4","NAME":"SHIV","DEPT4":"LIB"},
{"ID":"5","NAME":"TIM","DEPT5":"SEC"}
]
Output
outArr ={
[{"ID":"1","NAME":"KEN","DEPT1":"CSE","REL":"AND"},
{"ID":"2","NAME":"MARK","DEPT2":"IT","REL":"AND"}], //Arr1
[{"ID":"3","NAME":"TOM","DEPT3":"ECE","REL":"OR"},
{"ID":"4","NAME":"SHIV","DEPT4":"LIB","REL":"OR"}], //Arr2
[{"ID":"5","NAME":"TIM","DEPT5":"SEC"}] //Arr3
}
Code:
let condArr=[],outArr,i=1;
inArr1.forEach(condt => {
let dept = Object.keys(condt)[0];
let tmparr = dept.split("D");
tmparr.shift()
condArr.push(tmparr)
});
inArr2.forEach(condt => {
if(condArr.includes(inArr2.D+i)){
i++;
outArr.push(inArr2);
}
});
Your code has a bit confused logic, i would suggest rather this
let inArr1 = [{"D1D2":"AND"},{"D3D4":"OR"},{"D5D6":"AND"}]
let inArr2 =[{"ID":"1","NAME":"KEN","DEPT1":"CSE"},
{"ID":"2","NAME":"MARK","DEPT2":"IT"},
{"ID":"3","NAME":"TOM","DEPT3":"ECE"},
{"ID":"4","NAME":"SHIV","DEPT4":"LIB"},
{"ID":"5","NAME":"TIM","DEPT5":"SEC"},
{"ID":"6","NAME":"TLA","DEPT6":"SEC"},
]
// first lets create object of ids as keys and conditions as values
const [keys, conditions] = inArr1.reduce((agg, cond, index) => {
Object.entries(cond).forEach(([key, value]) => {
key.split('D').forEach(v => { if (v) agg[0][v] = { value, index }})
agg[1].push([])
})
return agg
}, [{}, []]) // {1: "AND", 2: "AND", 3: "OR", 4: "OR"}
conditions.push([])
// and now just map over all elements and add condition if we found id from the keys
inArr2.forEach(item => {
const cond = keys[item.ID]
if (cond) conditions[cond.index].push({...item, REL: cond.value})
else conditions[conditions.length - 1].push(item)
})
const res = conditions.filter(v => v.length)
console.log(res)
You could store the goups by using the ID and use new objects.
let inArr1 = [{ D1D2: "AND" }, { D3D4: "OR" }],
inArr2 = [{ ID: "1", NAME: "KEN", DEPT1: "CSE" }, { ID: "2", NAME: "MARK", DEPT2: "IT" }, { ID: "3", NAME: "TOM", DEPT3: "ECE" }, { ID: "4", NAME: "SHIV", DEPT4: "LIB" }, { ID: "5", NAME: "TIM", DEPT5: "SEC" }],
groups = inArr1.reduce((r, o) => {
Object.entries(o).forEach(([k, REL]) => {
var object = { REL, group: [] };
k.match(/[^D]+/g).forEach(id => r[id] = object);
});
return r;
}, {}),
grouped = inArr2.reduce((r, o) => {
var { REL, group } = groups[o.ID] || {};
if (group) {
if (!group.length) r.push(group);
group.push(Object.assign({}, o, { REL }));
} else {
r.push([o]);
}
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
can try other solution:
let inArr1 = [{ D1D2: "AND" }, { D3D4: "OR" }, { D6D7: "XOR" }];
let inArr2 = [
{ ID: "1", NAME: "KEN", DEPT1: "CSE" },
{ ID: "2", NAME: "MARK", DEPT2: "IT" },
{ ID: "3", NAME: "TOM", DEPT3: "ECE" },
{ ID: "4", NAME: "SHIV", DEPT4: "LIB" },
{ ID: "5", NAME: "TIM", DEPT5: "SEC" },
{ ID: "9", NAME: "BAR", DEPT5: "XYZ" },
{ ID: "6", NAME: "FOO", DEPT5: "XYZ" },
];
let unmatchedArr = []
let matchedArr = inArr2.reduce((acc, obj) => {
// getting index matched from inArr1 objects key
const indexMatched = getIndexMatch(obj.ID);
// creating index if not exists
if (!acc[indexMatched] && indexMatched !== null) acc[indexMatched] = [];
// if some index matched it merge current obj with DEL property with inArr1[indexMatched] key => value
return indexMatched !== null
? acc[indexMatched].push({
...obj,
DEL: inArr1[indexMatched][Object.keys(inArr1[indexMatched])[0]]
})
// pushing on unmatchedArr
: unmatchedArr.push(obj)
, acc
}, []);
function getIndexMatch(id) {
for (const [index, obj] of inArr1.entries()) {
for (const key of Object.keys(obj)) {
// spliting only digits of the current key of object
if (key.match(/\d/g).includes(id)) return index; // returning index of inArr1 if is included
}
}
return null;
}
// merging arrays
const result = [...matchedArr, unmatchedArr];
console.log(result);

Ignoring "The" when sorting an object array

I have an array:
var array = {
"mylist": [
{
"item1": "The Ba",
"id": 1
},
{
"item1": "Hurts Ama",
"id": 2
}
]
}
and to sort them I am using the following function:
function sortByItem(a,b) {
if (a.item1 < b.item1)
return -1;
if (a.item1 > b.item1)
return 1;
return 0;
}
which gives me the output
[Hurts Ama, The Ba]
However, I don't want "The" to be included when comparing, so that the output would actually be:
[Ba, Hurts Ama]
You could replace the at the beginning with following whitespace.
var array = [{ item1: "The Ba", id: 1 }, { item1: "Hurts Ama", id: 2 }, { item1: "Thereafter ", id: 3 }];
array.sort(function (a, b) {
function getStripped(s) { return s.replace(/^the\s+/i, ''); }
return getStripped(a.item1).localeCompare(getStripped(b.item1));
});
console.log(array)
.as-console-wrapper { max-height: 100% !important; top: 0; }
first map a transform function every of your objects that removes any "The " then run your sort by
function transform(item) {
return {
id: item.id,
item: item.replace("The ","")
}
}
var list =
[
{
"item": "The Ba",
"id": 1
},
{
"item": "Hurts Ama",
"id": 2
}
]
list.map(transform).sort(sortByItem)

Categories

Resources