How to find parent nodes path from flattened array? - javascript

I have flattened hierarchy array as follows.
I'd like to get path to the specific node as array.
var arr = [
{id:1, parent_id:null},
{id:2, parent_id:1},
{id:3, parent_id:null},
{id:4, parent_id:2},
{id:5, parent_id:4}
]
getPath(arr, 5) return [1,2,4]
Do you have any suggestion?

You could use a hash table for all id and their parents and iterate until no parent is available.
function getPath(nodes, id) {
var parents = Object.create(null),
path = [];
nodes.forEach(function (n) {
parents[n.id] = n.parent_id;
});
while (id = parents[id]) {
path.unshift(id);
}
return path;
}
var array = [{ id: 1, parent_id: null }, { id: 2, parent_id: 1 }, { id: 3, parent_id: null }, { id: 4, parent_id: 2 }, { id: 5, parent_id: 4 }];
console.log(getPath(array, 5)); // [1, 2, 4]

Here is my simple answer for what you want to achieve:
var arr = [
{id:1, parent_id:null},
{id:2, parent_id:1},
{id:3, parent_id:null},
{id:4, parent_id:2},
{id:5, parent_id:4}
];
var res=[];
function getPath(arr, x){
for(var i=0; i<arr.length; i++){
if(arr[i].id== x){
getPath(arr, arr[i].parent_id);
if(arr[i].parent_id !== null){
res.push(arr[i].parent_id);
}
}
}
}
getPath(arr, 5);
console.log(res);
I have just used recursion and it works great.

Recursive function to get the desired output.
var arr = [
{id:1, parent_id:null},
{id:2, parent_id:1},
{id:3, parent_id:null},
{id:4, parent_id:2},
{id:5, parent_id:4}
]
let resultArr = [];
function getPath(arr, num) {
for(var i in arr) {
if(arr[i].id === num) {
//console.log(num);
getPath(arr, arr[i].parent_id);
resultArr.push(num);
break;
}
}
return resultArr.slice(0, (resultArr.length - 1));
}
var res = getPath(arr, 5);
console.log(res);

You could define a recursive function that finds the item in the array with an id value matching the provided key parameter, and then checks if the current item has a parent_id truthy value.
If there is a parent_id:
The function will define results as an array if it wasn't passed into the function, and will then add the current item's parent_id to the front of the results array. Next, it will call itself with the following three parameters:
The starting array (arr).
The current item's parent_id as the key to look at next.
The results array as the array to return when completely done.
If there is no parent_id:
The function will instead return the results array.
var arr = [
{id:1, parent_id:null},
{id:2, parent_id:1},
{id:3, parent_id:null},
{id:4, parent_id:2},
{id:5, parent_id:4}
];
function getPath(arr, key, results) {
var nextObj = arr.filter(function(obj){
return obj.id == key;
})[0];
if(nextObj && nextObj.parent_id) {
results = typeof results !== 'object' ? [] : results;
var nextKey = nextObj.parent_id;
results.unshift(nextKey);
return getPath(arr, nextKey, results);
}
return results;
}
console.log(getPath(arr, 5));

Related

Combine unknown sub-arrays of objects from an array of objects and filter it

a simpler way to re-create the output of this code, where if sub arr0 is not null, it then combines it and filter desired output by the key code.
Thank you in advance!
let array = [
{
id: 1,
text: 'stuff1',
arr0: [
{id:1, code: 'imacode1'},
{id:2, code: 'imacode2'},
]
},
{
id: 2,
text: 'stuff2',
arr0: [
{id:3, code: 'imacode3'},
{id:4, code: 'imacode4'},
]
},
{
id: 3,
text: 'stuff3',
arr0: []
}
]
let arr = [];
for(let i of array){
if(i.arr0.length !== 0){
arr.push(i.arr0)
}
}
arr = arr.flat()
for(let j of arr){
if(j.code === 'imacode2'){
let code = arr.filter(j=>j.code!=='imacode2')
code = code.map(({code}) => code)
console.log(code)
}
}
edit: added snippet
You oan use Array.flatMap() along with with Array.filter() to get the result you wish, first we use .flatMap() to create an array including all items in each arr0.
We then use .filter() to only include the desired items, using a custom (modifiable) filter function, in this case removing any item with a code of 'imacode2'.
let array = [ { id: 1, text: 'stuff1', arr0: [ {id:1, code: 'imacode1'}, {id:2, code: 'imacode2'}, ] }, { id: 2, text: 'stuff2', arr0: [ {id:3, code: 'imacode3'}, {id:4, code: 'imacode4'}, ] }, { id: 3, text: 'stuff3', arr0: [] } ]
// Change filter as desired..
const filterProc = ({id, code}) => code !== 'imacode2';
const result = array.flatMap(el => el.arr0 || []).filter(filterProc).map(({code}) => code);
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Return a subset of an array of objects that matches a property of an another array of objects

inputs:
const parentArray = [
{id:1, name:'foo'},
{id:2, name:'bar'},
{id:4, name:'foobar'},
{id:6, name:'barfoo'}
]
const childArray = [
{parent_id:1, prop:'prop1'},
{parent_id:2, prop:'prop2'},
{parent_id:3, prop:'prop3'},
{parent_id:4, prop:'prop4'},
{parent_id:5, prop:'prop5'}
];
output:
const resultingArray = [
{id:1, name:'foo'},
{id:2, name:'bar'},
{id:4, name:'foobar'}
]
I want to compare the properties id and parent_id from both arrays and return a subset of parentArray for the matching properties
I've tried to filter them out but not having success, using lodash
You could take a Set for the wanted parents and filter the parent array.
var parents = [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }],
children = [{ parent_id: 1, prop: 'prop1' }, { parent_id: 3, prop: 'prop3' }],
wanted = new Set(children.map(({ parent_id }) => parent_id)),
result = parents.filter(({ id }) => wanted.has(id));
console.log(result);
You can do so with a combination of Array.filter() and Array.some() in the following way.
const resultingArray = parentArray
.filter(x => childArray.some( y => y.parent_id===x.id));
Check this JS bin
We can use a Set as a lookup table for the parent_id data from the child and then use Array.prototype.filter to filter through the parent entries and use Set#has to check if the id is contained in the Set:
const parentArray = [{id:1, name:'foo'},{id:2, name:'bar'}, {id:4, name:'foo'},{id:6, name:'bar'}]
const childArray = [
{parent_id:1, prop:'prop1'},
{parent_id:2, prop:'prop2'},
{parent_id:3, prop:'prop3'},
{parent_id:4, prop:'prop4'},
{parent_id:5, prop:'prop5'}
];
function findSubSet(){
const lookup = new Set(childArray.map(({parent_id}) => parent_id));
return parentArray.filter(p => lookup.has(p.id));
}
console.log(findSubSet(parentArray, childArray));
You can use reduce & findIndex. In the reduce callback use findIndex to check if there exist same id.If id exist it will return the index & if not then it will return -1. So if index is not -1 then you can push the value to accumulator(acc)
const parentArray = [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}]
const childArray = [{
parent_id: 1,
prop: 'prop1'
},
{
parent_id: 2,
prop: 'prop2'
},
{
parent_id: 3,
prop: 'prop3'
}
]
let filteredData = parentArray.reduce(function(acc, curr) {
let getIndexFromChild = childArray.findIndex(function(item) {
return curr.id === item.parent_id
});
if (getIndexFromChild !== -1) {
acc.push(curr)
}
return acc;
}, []);
console.log(filteredData)
As previously mentioned, your example is unclear, but filtering an array using another array, assuming you want to use the properties id from parentArray and parent_id from childArray, then I would use this:
resultingArray = childArray.filter(c=> parentArray.find(p => p.id === c.parentId);
You can use a mixture of filter and some to get the matching values:
const parentArray = [{id:1, name:'foo'},{id:2, name:'bar'}]
const childArray = [
{parent_id:1, prop:'prop1'},
{parent_id:3, prop:'prop3'}
]
let result = parentArray.filter(i => childArray.some(j => j.parent_id == i.id))
console.log(result)

The operation of the JavaScript array and object

oldList:
var oldList = [
{id:1, time:'2018-02-06 09:00-10:00', title:'aa'},
{id:2, time:'2018-02-06 11:00-12:00', title:'bb'},
{id:3, time:'2018-02-07 10:00:02', title:'cc'},
{id:4, time:'2018-02-07 09:00-10:00', title:'dd'}
];
console.log(oldList);
Desired:
var newList = [
{
'2018-02-06' : [
{id:1, time:'2018-02-06 09:00-10:00', title:'aa'},
{id:2, time:'2018-02-06 11:00-12:00', title:'bb'},
]
},
{
'2018-02-07' : [
{id:4, time:'2018-02-07 09:00-10:00', title:'dd'},
{id:3, time:'2018-02-07 10:00:02', title:'cc'},
]
},
];
console.log(newList);
How can I get the following result from this array and object?
I haven't found a good solution at the moment。
You can use reduce for this.
var oldList = [{
id: 1,
time: '2018-02-06 09:00-10:00',
title: 'aa'
},
{
id: 2,
time: '2018-02-06 11:00-12:00',
title: 'bb'
},
{
id: 3,
time: '2018-02-07 10:00:02',
title: 'cc'
},
{
id: 4,
time: '2018-02-07 09:00-10:00',
title: 'dd'
}
];
var newList = oldList.reduce(function(c, i) {
let t = i.time.split(" ")[0];
c[t] = c[t] || [];
c[t].push(i);
return c;
}, {});
console.log( newList );
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
you can use lodash to do this.
var newList = _.groupBy(oldList, function(o) {
return o.time.split(" ")[0];
});
var newList = {};
for (var i = 0; i < oldList.length; i++) {
var item = oldList[i];
var key = item.time; //here you can transform the key as you like (ie remove time)
if (newList[key] == null) {
newList[key] = [];
}
newList[key].push(item );
}
I created a dictionary. For each item in your old list I check if in the new list exist a key with your timestamp. If not, create a new entry with an empty array. Then, in both case, push your item into the specific array
Here's a solution without using reduce.
var oldList = [{
id: 1,
time: '2018-02-06 09:00-10:00',
title: 'aa'
}, {
id: 2,
time: '2018-02-06 11:00-12:00',
title: 'bb'
}, {
id: 3,
time: '2018-02-07 10:00:02',
title: 'cc'
}, {
id: 4,
time: '2018-02-07 09:00-10:00',
title: 'dd'
}];
var uniqueDates = []
for (i in oldList) {
if (uniqueDates.indexOf(oldList[i]['time'].split(' ')[0]) == -1) {
uniqueDates.push(oldList[i]['time'].split(' ')[0])
}
}
var newList = []
for (i in uniqueDates) {
var val = {}
val[uniqueDates[i]] = []
for (j in oldList) {
if(oldList[j]['time'].split(' ')[0] == uniqueDates[i]){
val[uniqueDates[i]].push(oldList[j])
}
}
newList.push(val)
}
console.log(newList)
But I like #Eddie's answer better

how to get a single field from array

I am using an array like below
var types = [{id:1, type:'Type 2'}, {id:2, type:'Type 5'}, {id:3, type:'Type 1'}, {id:4, type:'Type 2'}];
I want to filter values based on Type and get that into a single dimension array. I tried using filter etc. but gets the filtered 2-dimensional array.
$filter('filter')(types, { type: 'Type 2' })
Result should be just id array without type like:
[{id:1}, {id:4}]
Try with Array#map after the filter the array
var types = [{id:1, type:'Type 2'}, {id:2, type:'Type 5'}, {id:3, type:'Type 1'}, {id:4, type:'Type 2'}];
console.log(types.filter(a=> a.type =='Type 2' ).map(a=> ({id : a.id})))
true to its naming convention filter() will not manipulate object; it selects a new subset array.
You can use Array.map()
$filter('filter')(types, { type: 'Type 2' }).map(function(x){
return { id : x.id };
})
Working Fiddle :http://jsfiddle.net/ADukg/12074/
You can either use a combination of filter and map. Or, you can use this amazing function called reduce which can single-handedly filter as well as map your array. Like this:
var types = [{id:1, type:'Type 2'}, {id:2, type:'Type 5'}, {id:3, type:'Type 1'}, {id:4, type:'Type 2'}];
var res = types.reduce(function(arr, obj) {
if(obj.type === 'Type 2') {
arr.push({
id: obj.id
})
}
return arr
}, [])
console.log(res)
Hi Vikas Vaidya you can also use the following code to solve this problem:
function filterArrayofObjects(objKey, objValue) {
var temp = [], count = -1;
for (var i in types) {
var flag = false;
var obj = {};
for (var j in types[i]) {
if (j === objKey && types[i][j] === objValue) {
flag = true;
continue;
}
obj[j] = types[i][j];
}
if (flag) {
count++;
temp[count] = obj;
}
}
return temp;
}
var result = filterArrayofObjects("type", "Type 2");
window.console.log(result);
You can use Underscore js. _.filter and _.map can help you to achieve desired results

Filter an array of objects and extract its property by using an array of keys

im having this problem which i cant wrap around my head,
much better if put in a code.
//Array of objects sample
var objects = [{id:1, name:'test1'}, {id:2, name:'test2'}, {id:3, name: 'test3'}, {id:4, name:'test4'}];
var arrayOfKeys = [3,1,4];
//extract object name property if its id property is equivalent to one of arrayOfKeys [3,1]
//var arrayOfKeys = [3,1,4];
//output sample: extractedName=['test3','test1','test4'];
i've been using filter and map but no avail, also tried nesting filter inside map im getting an arrays of array and inside is a single object.
You could map the objects and ren map the wanted keys for getting the name.
var objects = [{ id: 1, name: 'test1' }, { id: 2, name: 'test2' }, { id: 3, name: 'test3' }],
arrayOfKeys = [3, 1],
result = arrayOfKeys.map((map => id => map.get(id).name)(new Map(objects.map(o => [o.id, o]))));
console.log(result);
I assume you need to map array numbers to id properties? Here's the code where you map through numbers and find inside your array to handle situations when there's no such id in objects array:
var objects = [{id:1, name:'test1'}, {id:2, name:'test2'}, {id:3, name: 'test3'}];
var arrayOfKeys = [3,1];
var res = arrayOfKeys.map(key => {
var found = objects.find(o => o.id == key);
return found ? found.name : false;
})
console.log(res)
let objects = [{id:1, name:'test1'}, {id:2, name:'test2'}, {id:3, name: 'test3'}, {id:4, name:'test4'}],
arrayOfKeys = [3,1,4];
let result = objects.reduce((res, obj) => { // loop over the array of objects
let index = arrayOfKeys.indexOf(obj.id); // check if the current object's id is in the array of keys
if(index !== -1) { // if it's there
res.push(obj.name); // then add the current object's name to the result array
arrayOfKeys.splice(index, 1); // remove its id from the array of keys so we won't search for it again (optional, slightly better than leaving it there)
}
return res;
}, []);
console.log(result);
I think you were on the right track with the filter. You can make it readable and concise with some.
var objects = [{id: 1,name: 'test1'}, {id: 2,name: 'test2'}, {id: 3,name: 'test3'}, {id: 4,name: 'test4'}],
arrayOfKeys = [3, 1, 4];
var result = objects.filter((x, i) => {
if (arrayOfKeys.some(k => x.id === k)) {
return true;
}
})
console.log(result.map(x=>x.name));
Something even shorter! would be
var objects = [{id: 1,name: 'test1'}, {id: 2,name: 'test2'}, {id: 3,name: 'test3'}, {id: 4,name: 'test4'}],arrayOfKeys = [3, 1, 4];
var result = objects.filter((x, i) => arrayOfKeys.some(k => x.id === k));
console.log(result.map(x=>x.name));

Categories