How to Group Array by Property and Reindex Result - javascript

I am trying to group items an array of items by a property and then reindex the result starting from 0.
The following function returns a grouped set of items.
groupItemBy(array, property) {
let hash = {},
props = property.split('.');
for (let i = 0; i < array.length; i++) {
let key = props.reduce( (acc, prop) => {
return acc && acc[prop];
}, array[i]);
if (!hash[key]) hash[key] = [];
hash[key].push(array[i]);
}
return hash;
}
The result is an array of arrays, and something like:
[{
"1193312":[
{
"description":"Item 1",
"number": "1193312"
}
],
"1193314":[
{
"itemDesc":"Item 2"},
"number": "1193314"
{
"description":"Item 3",
"number": "1193314"
}
],
etc...
}]
From here I'd like to map 1193312 to 0, and 1193314 to 1, etc.
I tried .filter(val => val) on the result, but that seemed to have no effect.

You need to use an intermediate key replacement:
function groupItemBy(array, property) {
let hash = {}
let props = property.split('.')
let keys = []
for (let i = 0; i < array.length; i++) {
let key = props.reduce((acc, prop) => {
return acc && acc[prop];
}, array[i]);
let subst = keys.indexOf(key)
if (subst === -1) {
keys.push(key)
subst = keys.length - 1
}
if (!hash[subst]) hash[subst] = [];
hash[subst].push(array[i]);
}
return hash;
}
[ https://jsfiddle.net/05t9141p/ ]

You could use map for the array part, and Object.values and reduce for the renumber part.
const data = [{
"1193312":[
{
"time":"2018-02-20",
"description":"Item 1",
"number": "1193312"
}
],
"1193314":[
{
"time":"2018-02-21",
"itemDesc":"Item 2",
"number": "1193314"
},{
"time":"2018-02-21",
"description":"Item 3",
"number": "1193314"
}
]
}];
const renumbered =
data.map((m) => Object.values(m).reduce((a,v,ix) => (a[ix] = v, a), {}));
console.log(renumbered);

var data = [{
"1193312":[
{
"description":"Item 1",
"number": "1193312"
}
],
"1193314":[
{
"itemDesc":"Item 2",
"number": "1193314"},
{
"description":"Item 3",
"number": "1193314"
}
]
}]
var newData = Object.keys(data[0]).map(function(key,index){
var newObj={};
newObj[index] = data[0][key];
return newObj;
});
console.log(newData);

If you can use ES6:
var arr = [{
"1193312":[
{
"time":"2018-02-20",
"description":"Item 1"
}
],
"1193314":[
{
"time":"2018-02-21",
"itemDesc":"Item 2"},
{
"time":"2018-02-21",
"description":"Item 3"
}
]
}];
var data = arr[0];
var res = Object.entries(data).map(([key, value]) => ({[key]: value}));
console.log(res);

Related

Get parent object after looping through nested array

I have a nested array I loop through every element in the array and read the value of key title then I check if the value includes a certain string if it includes the search string that value will be pushed to another array then I sort the new array by occurrence of the search string so that the index position of every element in the new array depends how similar the array element string is with the search string and also the array elements will be sorted by ascending letter order. Now this whole procedure works fine. What I want is once I get the sorted new array I want to loop though it then get the object containing the new arrays element as a title. How can I achieve this. Read the comments in my code to better understand my question. Thanks in advance.
note: When retrieving the object it should keep the index position of the newly created array.
const allArr = [
[{
"id": "1",
"title": "blaha"
}, {
"id": "2",
"title": "blahe"
}, {
"id": "3",
"title": "dhs"
}],
[{
"id": "4",
"title": "blahc"
}, {
"id": "5",
"title": "shg"
}]
]
const searchTerm = 'blah'
let existsArr = []
let tempArr = []
for (var i = 0; i < allArr.length; i++) {
const allAds = allArr[i]
for (var j = 0; j < allAds.length; j++) {
if (allAds[j].title.toLowerCase().includes(searchTerm.toLowerCase())) {
existsArr.push(allAds[j].title)
}
}
}
tempArr = existsArr.filter(a => a.toLowerCase().includes(searchTerm))
const startsWith = (string, value) => string.substring(0, value.length).toLowerCase() === value
const sortByOccurance = JSON.stringify(tempArr.sort((a, b) => {
const aStartsWith = startsWith(a, searchTerm)
const bStartsWith = startsWith(b, searchTerm)
if (aStartsWith && !bStartsWith) {
return -1
} else if (!aStartsWith && bStartsWith) {
return 1;
}
return b > a ? -1 : 1
}))
console.log(sortByOccurance)
//now I want to get the object of every title found in sortByOccurance from allArr
//expected output:
//[{"id": "1", "title": "blaha"}, {"id": "4", "title": "blahc"}, {"id": "2", "title": "blahe"}]
Array.flat flattens the array a level (or more). That makes it easier to find an item.title === term. So now we can loop over array and build our result array.
Update: not using Array.flat to allow for duplicate names with different id. Instead, we search for the first match (a "deep" search) and then delete it so next time will find next item.
const allArr = [
[{
"id": "1",
"title": "blaha"
}, {
"id": "2",
"title": "blahe"
}, {
"id": "3",
"title": "dhs"
}],
[{
"id": "4",
"title": "blahc"
}, {
"id": "5",
"title": "shg"
}],
[{
"id": "6",
"title": "blaha"
}]
]
const searchTerm = 'blah'
let existsArr = []
let tempArr = []
for (var i = 0; i < allArr.length; i++) {
const allAds = allArr[i]
for (var j = 0; j < allAds.length; j++) {
if (allAds[j].title.toLowerCase().includes(searchTerm.toLowerCase())) {
existsArr.push(allAds[j].title)
}
}
}
tempArr = existsArr.filter(a => a.toLowerCase().includes(searchTerm))
const startsWith = (string, value) => string.substring(0, value.length).toLowerCase() === value
const sortByOccurance = JSON.stringify(tempArr.sort((a, b) => {
const aStartsWith = startsWith(a, searchTerm)
const bStartsWith = startsWith(b, searchTerm)
if (aStartsWith && !bStartsWith) {
return -1
} else if (!aStartsWith && bStartsWith) {
return 1;
}
return b > a ? -1 : 1
}))
function find_deep(arr, term) {
for (var i = 0; i < arr.length; i++) {
var value = arr[i]
if (Array.isArray(value)) {
var res = find_deep(value, term)
if (res) {
return res;
}
}
if (value.title === term) {
return value;
}
}
return null;
}
console.log(sortByOccurance)
var result = [];
JSON.parse(sortByOccurance).forEach(function(term) {
var found = find_deep(allArr, term)
if (found) {
result.push({ ...found
})
delete found.title; // <--- changes original allArr.
}
})
console.log(result)
.as-console-wrapper {
max-height: 100% !important
}

Merge array of arrays based sub array index as keys (NodeJS/Javascript)

How to write a code that would merge my list in the following way? Performance is important. I want to convert the following array:
"list": [
[
"marketing",
"page_sections",
"PageOne"
],
[
"marketing",
"page_sections",
"PageTwo"
],
[
"webapp",
"page",
"pageone"
],
[
"webapp",
"page",
"pagetwo"
],
To the following format:
[
{
name: "marketing",
path: "marketing/",
children: [
{
name: "page_sections",
path: "marketing/page_sections",
children: [
{
name: "pageOne",
path: "marketing/page_sections/pageOne",
children: []
},
{
name: "pageTwo",
path: "marketing/page_sections/pageTwo",
children: []
},
}
],
},
{
name: "webapp",
path: "webapp/"
children: [
{
name: "page",
path: "webapp/page/"
children: [
{
name: "pageone",
path: "webapp/page/pageone"
children: []
},
{
name: "pagetwo",
path: "webapp/page/pagetwo"
children: []
},
}
]
},
]
The first index of sub array is parent, second index is child of parent, third index is child of second index (and so on).
The shortest approach is to iterate the nested names and look for an object with the same name. If not exist, create a new object. Return the children array as new level.
This approach features Array#reduce for iterating the outer array of data and for all inner arrays.
const
data = [["marketing", "page_sections", "PageOne"], ["marketing", "page_sections", "PageTwo"], ["webapp", "page", "pageone"], ["webapp", "page", "pagetwo"]],
result = data.reduce((r, names) => {
names.reduce((level, name, i, values) => {
let temp = level.find(q => q.name === name),
path = values.slice(0, i + 1).join('/') + (i ? '' : '/');
if (!temp) level.push(temp = { name, path, children: [] });
return temp.children;
}, r);
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Looking at the source, and your expected result.
What I would do is loop the list, and then do another loop inside the list. Mix this with Array.find..
Eg..
const data = {list:[
["marketing","page_sections","PageOne"],
["marketing","page_sections","PageTwo"],
["webapp","page","pageone"],
["webapp","page","pagetwo"]]};
function makeTree(src) {
const root = [];
for (const s of src) {
let r = root;
let path = '';
for (const name of s) {
path += `${name}/`;
let f = r.find(k => k.name === name);
if (!f) r.push(f = {name, path, children: []});
r = f.children;
}
}
return root;
}
console.log(makeTree(data.list));
.as-console-wrapper {
min-height: 100%;
}
You can do the following,
list= [
[
"marketing",
"page_sections",
"PageOne"
],
[
"marketing",
"page_sections",
"PageTwo"
],
[
"webapp",
"page",
"pageone"
],
[
"webapp",
"page",
"pagetwo"
],
];
getChildrenItem = (arr) => {
if(arr.length === 1) {
return { name: arr[0], children: []};
} else {
return { name: arr.splice(0,1)[0], children: [getChildrenItem([...arr])]};
}
}
merge = (srcArr, newObj) => {
const {name, children} = newObj;
let index = srcArr.findIndex(item => item.name === name);
if( index> -1) {
children.forEach(item => merge(srcArr[index].children, item))
return ;
} else {
srcArr.push(newObj);
return;
}
}
allObj = [];
list.forEach(item => {
let tempObj = getChildrenItem([...item]);
merge(allObj, tempObj);
});
console.log(allObj);
If Performance is an issue, I think this is one of the best solutions.
let list = [
["marketing", "page_sections", "PageOne"],
["marketing", "page_sections", "PageTwo"],
["webapp", "page", "pageone"],
["webapp", "page", "pagetwo"],
];
const dt = {};
const pushToOBJ = (Object, name) => {
if (Object[name]) return Object[name];
Object[name] = {
name,
children: {},
};
return Object[name];
};
for (let i = 0; i < list.length; i++) {
let subArray = list[i];
let st = pushToOBJ(dt, subArray[0]);
for (let j = 1; j < subArray.length; j++) {
st = pushToOBJ(st.children, subArray[j]);
}
}
let result = [];
const convertObjToChildArray = (obj) => {
if (obj === {}) return [];
let arr = Object.values(obj);
for (let i = 0; i < arr.length; i++) {
arr[i].children = convertObjToChildArray(arr[i].children);
}
return arr;
};
result = convertObjToChildArray(dt);
console.log(result);
No Use of JS find function, which already has O(n) Complexity.

How to iterate children from array in javascript?

i Have an array that could be :
arr1 = ["node1","children1","children1.1","children1.1.1"]
or it could be
arr2 = ["node1","children1"]
and I want to make it in this json format :
const data_arr1 = [{
title: "Node 1",
childNodes: [
{ title: "Childnode 1" ,
childNodes: [
{
title: "Childnode 1.1",
childNodes: [
{ title: "Childnode 1.1.1" }
]
}
]
}
]
}];
var data_arr2 = {title:"node1",childNodes:{title:"children1"}}
I have do like that but i can't have the right format in iterative way :
BuildJson = (items) => {
const elements = items.split(",");
let result = {};
var children = []
result["title"] = elements[0];
elements.shift()
if(elements.length>1) {
for(var i=0;i<elements.length;i++){
elements.map((el,idx)=> {
children.push({title:el})
})
}
result["ChildNodes"] = children
}
Please how can I fix this algorithm ?
I suggest you to use recursive function.
I made you an example:
const t = ["lvl1", "lvl2", "lvl3"];
const r = (array) => {
if (array.length === 1) {
return {
title: array[0]
};
}
if (array.length > 1) {
return {
title: array[0],
childNode: r(array.slice(1))
};
}
};
r(t);
r(t) returned the following JSON:
{
"title": "lvl1",
"childNode": {
"title": "lvl2",
"childNode": {
"title": "lvl3"
}
}
}
const arr1 = ["node1","children1","children1.1","children1.1.1"]
const createArray = (arr, i = 0) => {
const obj = {
title: arr[i]
};
if (i < arr.length - 1) {
obj.childNodes = createArray(arr, ++i);
}
return [obj];
}
const newArr = createArray(arr1);
console.log(newArr);

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);

Change object in array's key name

arr = [
{"id":"1"},
{"id":"2"}
];
For some reason I want to change the "id" to "uid". I am stuck here
arr.forEach(function(i){
});
arr = [{
"id": "1"
},
{
"id": "2"
}
];
arr.forEach(function(i) {
i.uid = i.id;
delete i.id;
});
console.log(arr);
This will modify arr. If you want a copy of arr that has the changed structure, follow Mritunjay's answer.
Just do like bellow:
arr = [{
"id": "1"
},
{
"id": "2"
}
];
arr = arr.map(function(obj) {
return {
"uid": obj.id
}
});
console.log(arr);
Here you go:
arr.map(function (a) {
a.uid=a.id;delete a.id;
return a;
});
This just goes through the array, renames it, and returns the value.
Snippet:
var arr = [{
"id": "1"
}, {
"id": "2"
}];
arr = arr.map(function(a) {
a['uid'] = a['id'];
delete a['id'];
return a;
});
console.log(arr);
You mentiond forEach so here's an answer with it.
arr.forEach(function (a) {
a.uid=a.id;delete a.id;
});
arr = [{
"id": "1"
},
{
"id": "2"
}
];
arr = arr.map(function(item, index) {
// forget about the index, e.g. running from 0 to arr.length - 1
return {
uid: item.id
};
});
console.log(arr);

Categories

Resources