Recursion in JavaScript - not recursing - javascript

I'm trying to get a recursion work in JavaScript. This is my code.
var getCategory = null;
getCategory = function (id, cats) {
if (!cats || !cats.length) return null;
for (var i = 0; i < cats.length; i++) {
var cat = cats[i];
if (cat && cat.Id == id) {
return cat;
}
else if (cat && cat.Children && cat.Children.length) {
return getCategory(id, cat.Children);
}
}
return null;
}
This code basically searches in the hierarchy of data for a specific element of a specific id.
Let's say that my sample data is:
var sampleData = [
{Id : 1, Children : [{Id:11, Children:[]}, {Id : 12, Children : []}]},
{Id : 2, Children : [{Id:21, Children:[]}, {Id : 22, Children : []}, {Id : 23, Children : []}]},
{Id : 3, Children : [{Id:31, Children:[]}, {Id : 32, Children : []}, {Id : 33, Children : []}]}
]
The problem is that when I call this function like:
getCategoriy(21, sampleData); //it gives null
even when get:
getCategiry(11, sampleData); //gives perfect object back
What am I doing wrong?

You're starting to loop through the cats, but if you see an entry that has children, you're recursing into that entry and then terminating your loop. That means you don't see any of the cats after that entry.
Instead of
return getCategory(id, cat.Children);
You need to call it and only return if it returns !null:
value = getCategory(id, cat.Children);
if (value) {
return value;
}

Related

How to create a sub-associative object from a list of array keys in JavaScript?

I have an object from user input. The keys to that object are separated by commas, and I just want to separate those keys and make the keys of the object.
The key_array below is dynamic from user input, generates a different array each time, below I give you an example.
I have shown the object in my code which you can see below. you can also see the output by running that code.
var main_array = {};
var key_array = {
'user,name' : 'user name',
'user,email' : 'Email address',
'order,id' : 123456,
'order,qty' : 2,
'order,total' : 300,
'order,product,0,name' : "product1",
'order,product,0,qty' : 1,
'order,product,0,price' : 100,
'order,product,1,name' : "product2",
'order,product,1,qty' : 1,
'order,product,1,price' : 200,
};
for (keys in key_array){
var value = key_array[keys];
// What do I do here to get the output I want?
main_array['[' + keys.split(",").join('][')+ ']'] = value;
}
console.log(main_array);
Running the code above will give you the following output which is incorrect. And the output I don't want.
{
[order][id]: 123456,
[order][product][0][name]: "product1",
[order][product][0][price]: 100,
[order][product][0][qty]: 1,
[order][product][1][name]: "product2",
[order][product][1][price]: 200,
[order][product][1][qty]: 1,
[order][qty]: 2,
[order][total]: 300,
[user][email]: "Email address",
[user][name]: "user name"
}
I want an output like JSON below, so please tell me how to do it.
{
"user":{
"email" : "Email address",
"name" : "user name"
},
"order":{
"id" : 123456,
"qty" : 2,
"total" : 300,
"product":[
{
"name" : "product1",
"price" : 100,
"qty" : 1
},{
"name" : "product2",
"price" : 200,
"qty" : 1
}
]
}
}
Note: Please do not use eval, as using eval in this way is terribly unreliable, bad work and unsafe. Because I get all my data from user input, the likelihood of abuse can increase.
Use Object.entries to go over key and values of object.
Split the key by , separator and then build the object.
While building object, make sure to merge the keys and values using mergeTo method.
Then convert the objects which has the numerical keys then convert to object using convertObjsToArray method.
var key_array = {
"user,name": "user name",
"user,email": "Email address",
"order,id": 123456,
"order,qty": 2,
"order,total": 300,
"order,product,0,name": "product1",
"order,product,0,qty": 1,
"order,product,0,price": 100,
"order,product,1,name": "product2",
"order,product,1,qty": 1,
"order,product,1,price": 200
};
const mergeTo = (target, obj) => {
Object.entries(obj).forEach(([key, value]) => {
if (typeof value === "object" && !Array.isArray(value)) {
if (!target[key]) {
target[key] = {};
}
mergeTo(target[key], obj[key]);
} else {
target[key] = value;
}
});
};
const convertObjsToArray = obj => {
Object.entries(obj).forEach(([key, value]) => {
if (typeof value === "object") {
if (Object.keys(value).every(num => Number.isInteger(Number(num)))) {
obj[key] = Object.values(value);
} else {
convertObjsToArray(obj[key]);
}
}
});
};
const res = {};
Object.entries(key_array).map(([key, value]) => {
const keys = key.split(",");
let curr = { [keys.pop()]: value };
while (keys.length > 0) {
curr = { [keys.pop()]: curr };
}
mergeTo(res, curr);
});
convertObjsToArray(res);
console.log(res);
You can create the objects and keys required from the string dynamically, take each key and split it to an array using split(','). Using each item in the array create the structure required. Assuming if a key is a number, then it's parent must be an array.
Object.keys(key_array).forEach(key => {
const path = key.split(',');
let current = main_array;
for (let i = 0; i < path.length - 1; i++) {
if (!current[path[i]]) {
current[path[i]] = path[i + 1] && !isNaN(path[i + 1]) ? [] : {};
}
current = current[path[i]];
}
current[path.pop()] = key_array[key];
});
console.log(main_array); // Desired result

How can I remove duplicates in an array of object?

I have an array which looks like this :
var array =
[
{
key : { id : 1 , pack : "pack 1"},
values : [
{
item : { id : 1 , name : "item1"},
itemP : {id : 2 , name : "itemP12"}
},
{
item : { id : 4 , name : "item4"},
itemP : {id : 2 , name : "itemP12"}
},
]
}
]
I want to remove duplicate itemP so with a function it will look like this :
var array =
[
{
key : { id : 1 , pack : "pack 1"},
values : [
{
item : { id : 1 , name : "item1"},
itemP : {id : 2 , name : "itemP12"}
},
{
item : { id : 4 , name : "item4"},
itemP : null
},
]
}
]
When I try I always have errors. It is possible to do this?
Update
I try to do this :
console.log(array.map(pack =>
pack.values.map((item) => {
var test = JSON.stringify(item)
var set = new Set(test)
return Array.from(set).map((item)=> JSON.parse(item))
}
)
))
Unexpected end of JSON input
I also try something will filter but it doesn't work:
console.log(this.package.map(pack => pack.values.filter(
(value, index , array) => array.itemP.indexOf(value) === index
)))
Instead of mapping every key property, I suggest cloning the whole structure and setting the object value as null in the cloned one, avoiding unintentionally mutating the original structure.
function nullifyDupes(array) {
const clone = JSON.parse(JSON.stringify(array));
const seen = {};
clone.forEach(pack => {
pack.values.forEach(items => {
for (const item in items) {
const id = items[item].id;
if (seen[id]) items[item] = null;
else seen[id] = true;
}
});
});
return clone;
}
const originalArray = [{
key : { id : 1 , pack : "pack 1"},
values : [{
item : { id : 1 , name : "item1"},
itemP : {id : 2 , name : "itemP12"}
},
{
item : { id : 4 , name : "item4"},
itemP : {id : 2 , name : "itemP12"}
}]
}];
const mutatedArray = nullifyDupes(originalArray);
console.log(mutatedArray);
To achieve expected result, use below option of using map
Loop array using map
Use nameArr to check duplicate and assigning null value
Loop values array and check the name in nameArr using indexOf and assign null
var array = [
{
key : { id : 1 , pack : "pack 1"},
values : [
{
item : { id : 1 , name : "item1"},
itemP : {id : 2 , name : "itemP12"}
},
{
item : { id : 4 , name : "item4"},
itemP : {id : 2 , name : "itemP12"}
}
]
}
]
console.log(array.map(v => {
let nameArr = []
v.values = v.values.map(val => {
if(nameArr.indexOf(val.itemP.name) !== -1){
val.itemP.name = null
}else{
nameArr.push(val.itemP.name)
}
return val
})
return v
}))
You can use map and an object to check if its already exist. Like
var obj = {}
and loop over values
var values = [
{
item : { id : 1 , name : "item1"},
itemP : {id : 2 , name : "itemP12"}
},
{
item : { id : 4 , name : "item4"},
itemP : {id : 2 , name : "itemP12"}
}
]
values.map((v) => {
if(!obj[v.itemP.id + '-' + v.itemP.name]) {
obj[v.itemP.id + '-' + v.itemP.name] = true;
return v;
}
return { item : v.item }
})
You can map your array elements to array objects which don't include your duplicates using .map(). For each iteration of .map() you can again use .map() for your inner values array to convert it into an array of objects such that the duplicates are converted to null. Here I have kept a seen object which keeps track of the properties seen and their stringified values. By looping over all the properties in your object (using for...of), you can work out whether or not the key-value pair has been seen before by using the seen object.
The advantage of this approach is that it doesn't just work with one property (ie not just itemP), but it will work with any other duplicating key-value pairs.
See example below:
const array = [{key:{id:1,pack:"pack 1"},values:[{item:{id:1,name:"item1"},itemP:{id:2,name:"itemP12"}},{item:{id:4,name:"item4"},itemP:{id:2,name:"itemP12"}}]}];
const seen = {};
const res = array.map(obj => {
obj.values = obj.values.map(vobj => {
for (let p in vobj) {
vobj[p] = seen[p] === JSON.stringify(vobj[p]) ? null : vobj[p];
seen[p] = seen[p] || JSON.stringify(vobj[p]);
}
return vobj;
});
return obj;
});
console.log(res);
For an approach which just removed itemP from all object in accross your array you can use:
const array = [{key:{id:1,pack:"pack 1"},values:[{item:{id:1,name:"item1"},itemP:{id:2,name:"itemP12"}},{item:{id:4,name:"item4"},itemP:{id:2,name:"itemP12"}}]}];
let itemP = "";
const res = array.map(obj => {
obj.values = obj.values.map(vobj => {
vobj.itemP = itemP ? null : vobj.itemP;
if('itemP' in vobj) {
itemP = itemP || JSON.stringify(vobj.itemP);
}
return vobj;
});
return obj;
});
console.log(res);

Fetch data into an array of objects according to another array

This code compares the first array names to see if there is duplicates, once the duplicates are found, these duplicates are compared to 2 others array created by pl and pl2, if duplicates are found between the original duplicates and pl or pl2, these new duplicates are showed in the console.
var names = ["Nancy", "Patrick", "George", "Hecker", "George", "Nancy",
"Robert", "Robert"]
var part1 = [
{height : 1.2, weight : 50, age: 55, name : "Nancy" },
{height : 2, weight : 60, age: 47, name : "Patrick" },
{height : 2, weight : 42, age: 24, name : "George" },
{height : 1.7, weight : 56, age: 58, name : "Hecker" },
{height : 1.1, weight : 39, age: 23, name : "Karin" }
]
var part2 = [
{height : 0.2, weight : 23, age: 45, name : "George" },
{height : 1.8, weight : 41, age: 29, name : "Moos" },
{height : 1.5, weight : 35, age: 25, name : "Ricard" }
]
setTimeout(function() {
var uniq = names
.map((name) => {
return {count: 1, name: name}
})
.reduce((a, b) => {
a[b.name] = (a[b.name] || 0) + b.count
return a
}, {})
var duplicates = Object.keys(uniq).filter((a) => uniq[a] > 1)
console.log(duplicates) //output array with Nancy and George
checkInArr(duplicates);
}, 500);
function checkInArr(duplicates) {
var pl = _.pluck(part1, "name");
var pl2 = _.pluck(part2, "name");
var dup = _.intersection(pl, duplicates);
var dup2 = _.intersection(pl2, duplicates);
dup.forEach(function(element) {
console.log(element)
//Here is where I'm stuck, element regroup the name that are the same in
both duplicates and part1
//Now I would like to fetch the other corresponding data in the part1 and
part2 arrays of objects according to the names returned by element
//in this exemple : element returns Nancy and George so i would like to get
Nancy and George infos of part1 (height, weight, age)
})
dup2.forEach(function(element) {
console.log(element)
//in this exemple : element returns only George so i would like to get
George infos of part2 (height, weight, age)
})
}
But, as it is written inside the code, I would like to get the full infos according to the name and both arrays in the console.
So the wanted result would be the object of the corresponding names and not just the names. For exemple : if element returns George, I would like not only the name but all the infos about George stored in part1 and then another object of all the infos about George stored in part2.
I've tried some things but with no success and also didn't found infos on the web.
Thanks for helping.
Fiddle : http://jsfiddle.net/nc2ac8ed/
You could change your checkInArr function to use a Set and filter to check the intersections.
Your duplicates is an array of names that you're interested in. By creating a Set out of those values we can quickly check if a "person" intersects:
const doesIntersect = nameSet.has(person.name);
This can be used in a filter to weed out the non-duplicates.
var names = ["Nancy", "Patrick", "George", "Hecker", "George", "Nancy", "Robert", "Robert"]
var part1=[{height:1.2,weight:50,age:55,name:"Nancy"},{height:2,weight:60,age:47,name:"Patrick"},{height:2,weight:42,age:24,name:"George"},{height:1.7,weight:56,age:58,name:"Hecker"},{height:1.1,weight:39,age:23,name:"Karin"}];
var part2=[{height:.2,weight:23,age:45,name:"George"},{height:1.8,weight:41,age:29,name:"Moos"},{height:1.5,weight:35,age:25,name:"Ricard"}];
var uniq = names
.map((name) => {
return {count: 1, name: name}
})
.reduce((a, b) => {
a[b.name] = (a[b.name] || 0) + b.count
return a
}, {})
var duplicates = Object.keys(uniq).filter((a) => uniq[a] > 1)
console.log(duplicates) //output array with Nancy and George *and Robert*
checkInArr(duplicates);
function checkInArr(duplicates) {
const dupSet = new Set(duplicates);
const dup = part1.filter(p => dupSet.has(p.name));
const dup2 = part2.filter(p => dupSet.has(p.name));
dup.forEach(function(element) {
console.log("dup", element)
})
dup2.forEach(function(element) {
console.log("dup2:", element)
})
}

How to get a single element array from a multiple element array of object?

This is my array :
mapping = [{sl_no : 1, type : "A", parent : ""},{sl_no : 2, type : "B", parent : "A"},{sl_no : 3, type : "D", parent : "B"}]
I want the array in the bellow format :
mapped_sl_no = [{sl_no : 1},{sl_no : 2},{sl_no : 3}]
Map is your friend...
var mapped = mapping.map(function(item) { return item.sl_no; });
I suggest to use Array#map() with a temporary object inside.
var mapping = [{ sl_no: 1, type: "A", parent: "" }, { sl_no: 2, type: "B", parent: "A" }, { sl_no: 3, type: "D", parent: "B" }],
mapped_sl_no = getMapped(mapping, 'sl_no');
function getMapped(array, k) {
return array.map(function (a) {
var o = {};
o[k] = a[k];
return o;
});
}
document.write('<pre>' + JSON.stringify(mapped_sl_no, 0, 4) + '</pre>');
Solution with map function:
var mapping = [{sl_no : 1, type : "A", parent : ""},{sl_no : 2, type : "B", parent : "A"},{sl_no : 3, type : "D", parent : "B"}];
var arr = mapping.map(function(obj){
return {'sl_no': obj.sl_no};
})
console.log(arr); // the output is: [{sl_no : 1},{sl_no : 2},{sl_no : 3}]
var mapped_sl_no = [];
mapping.forEach(function(mapVal){
mapped_sl_no.push({sl_no:mapVal.sl_no});
});
This should do it for you.
Else you can use libraries like underscore(http://underscorejs.org/) or lowdash(https://lodash.com/)

How see if array contains object without using JQuery

I have an array that looks like this:
var myArray = [
{'id' : 1, 'name' : 'test1'},
{'id' : 2, 'name' : 'test2'},
{'id' : 3, 'name' : 'test3'}
];
Then I have a variable that contains some id:
var someId = 2;
How can I check if myArray contains an Object, who's id is equal to someId?
You can use the .some() method:
var isThere = myArray.some(function(element) {
return element.id == someId;
});
The .some() method returns a boolean true if the callback returns true for some element. The iteration stops as soon as that happens.
If you want the element in the array and not just a yes/no answer, you can pass the same kind of callback to .find():
var theElement = myArray.find(function(element) {
return element.id == someId;
});
When that callback returns true, the iteration stops and .find() returns the element itself. If none is found, it returns undefined.
You can try following
var filteredArray = myArray.filter(function(item) {
return item.id == someId
});
if(filteredArray.length > 0) {
console.log("Found");
}
Vanilla JS:
var myArray = [
{'id' : 1, 'name' : 'test1'},
{'id' : 2, 'name' : 'test2'},
{'id' : 3, 'name' : 'test3'}
];
function findby(arr, key, val){
for(var i=0; i<arr.length; i++){
if(arr[i][key] === val){
return arr[i];
}
}
return null;
}
var found = findby(myArray, "id", 2);
alert(found.name);

Categories