i need a help how can i treat maximum call stack? - javascript

Thanks i fixed some sentence by advice. my code is like that,
i wanna find object with id. but if not, I want to return 'null'
function ha7(arr, id) { // i wanna find object with id
let result = [];
for(let i = 0 ; i < arr.length ; i++) {
if(arr[i].id === id) {
return arr[i] // found id, then return included object
}
else if(Array.isArray(arr[i].children)){ // but , its array
// let ar = ha7(arr[i].children, id)
result.push(...arr[i].children) // i put 'arr[i].children' to variables
}
}
if (result.id === id) {
return result // find object with id in inner
} else {
return ha7(result, id) // cant find. then go ahead!
}
return null // all of none exist id is return null
}
it is testing array.
let input = [
{
id: 1,
name: 'johnny',
},
{
id: 2,
name: 'ingi',
children: [
{
id: 3,
name: 'johnson',
},
{
id: 5,
name: 'steve',
children: [
{
id: 6,
name: 'lisa',
},
],
},
{
id: 11,
},
],
},
{
id: '13',
},
];
output = ha7(input, 5);
console.log(output); // --> { id: 5, name: 'steve', children: [{ id: 6, name: 'lisa' }] }
output = ha7(input, 99);
console.log(output); // --> null
I tried a lot of trial, like that. i wanna know.
how can i treat maximum call stack ?
and i wanna return 'null' value.
function ha7(arr, id) { // i wanna find object with id
let result = [];
for(let i = 0 ; i < arr.length ; i++) {
if(arr[i].id === id) {
return arr[i] // found id, then return included object
}
else if(Array.isArray(arr[i].children)){ // but , its array
// let ar = ha7(arr[i].children, id)
result.push(...arr[i].children) // i put 'arr[i].children' to variables
}
}
if (result.id === id) {
return result // find object with id in inner
} else {
return ha7(result, id) // cant find. then go ahead!
}
return null // all of none exist id is return null
}
let input = [
{
id: 1,
name: 'johnny',
},
{
id: 2,
name: 'ingi',
children: [
{
id: 3,
name: 'johnson',
},
{
id: 5,
name: 'steve',
children: [
{
id: 6,
name: 'lisa',
},
],
},
{
id: 11,
},
],
},
{
id: '13',
},
];
output = ha7(input, 5);
console.log(output); // --> { id: 5, name: 'steve', children: [{ id: 6, name: 'lisa' }] }
output = ha7(input, 99);
console.log(output); // --> null

This code is the problem:
if (result.id === id) {
return result // find object with id in inner
} else {
return ha7(result, id) // cant find. then go ahead!
}
Two lines above this you initialize result as an array. Then in this conditional test you treat the array result as if it were an object. So, since result.id does not equal id, the else condition recurses for ever and ever.
I've taken a different, more functional approach to the task.
filter the array on the id
If there is a length then at least one was found
Return the first one
Next filter out all the objects with children
Then create an array (with .map() that only includes the children
This will create an array of arrays, so must flatten it
If there are no children, then id was not found
Return null
Recurse the children
let input=[{id:1,name:"johnny"},{id:2,name:"ingi",children:[{id:3,name:"johnson"},{id:5,name:"steve",children:[{id:6,name:"lisa"}]},{id:11}]},{id:"13"}];
function ha7(arr, id) {
let found = arr.filter(o => o.id === id);
if (found.length) return found[0]; // return first match
let children = arr.filter(o=>!!o.children).map(c=>c.children).flat();
if(!children.length) return null;
return ha7(children, id);
}
output = ha7(input, 5);
console.log(output); // --> { id: 5, name: 'steve', children: [{ id: 6, name: 'lisa' }] }
output = ha7(input, 99);
console.log(output); // --> null

Related

Set object property in an array true/false, whether the id matches with any id from another array of objects

So first, here's a simple snippet to demonstrate what I mean exactly, and what I have tried.
let array_1 = [
{ id: 1, name: 'Peter' },
{ id: 2, name: 'John' },
{ id: 3, name: 'Andrew' },
{ id: 4, name: 'Patrick' },
{ id: 5, name: 'Brian' }
];
let array_2 = [
{ id: 1, name: 'not Peter' },
{ id: 80, name: 'not John' },
{ id: 3, name: 'not Andrew' },
{ id: 40, name: 'not Patrick' },
{ id: 5, name: 'not Brian' }
];
array_1.forEach(item_1 => {
for (let i = 0; i < array_2.length; i++) {
item_1.matches = array_2[i].id === item_1.id
}
});
console.log('matched_array', array_1);
The goal here is to add the matches property to each object in array_1 and set it to true/false, based on whether the id matches with any other id from array_2.
In this current example, the result of the matches properties should go like this: true - false - true - false - true. But my current code only sets this property correctly in the last element of the array (array_1).
Obviously it's because my code is not entirely correct, and that's where I'm stuck.
You could first create one object with reduce method that you can then use as a hash table to check if the element with the same id exists in the array 2.
let array_1=[{"id":1,"name":"Peter"},{"id":2,"name":"John"},{"id":3,"name":"Andrew"},{"id":4,"name":"Patrick"},{"id":5,"name":"Brian"}, {"id":6,"name":"Joe"}]
let array_2=[{"id":1,"name":"not Peter"},{"id":80,"name":"not John"},{"id":3,"name":"not Andrew"},{"id":40,"name":"not Patrick"},{"id":5,"name":"not Brian"}]
const o = array_2.reduce((r, e) => (r[e.id] = true, r), {})
const result = array_1.map(e => ({ ...e, matches: o[e.id] || false}))
console.log(result)
I would first collect the ids of array_2 in a Set, sets have a O(1) lookup time so checking if an id is in this set is fast. Then iterate over array_1 and check if the id is present in the created set using has().
let array_1 = [
{ id: 1, name: 'Peter' },
{ id: 2, name: 'John' },
{ id: 3, name: 'Andrew' },
{ id: 4, name: 'Patrick' },
{ id: 5, name: 'Brian' }
];
let array_2 = [
{ id: 1, name: 'not Peter' },
{ id: 80, name: 'not John' },
{ id: 3, name: 'not Andrew' },
{ id: 40, name: 'not Patrick' },
{ id: 5, name: 'not Brian' }
];
const array_2_ids = new Set(array_2.map(item_2 => item_2.id));
array_1.forEach(item_1 => item_1.matches = array_2_ids.has(item_1.id));
console.log('matched_array', array_1);
Your current code doesn't work because the for-loop will update the item_1.matches property for each element in array_2. This means you are overwriting the property each time. This in turn will effectivly result in item_1 only being checked against the last item in array_2.
To make your code work this:
array_1.forEach(item_1 => {
for (let i = 0; i < array_2.length; i++) {
item_1.matches = array_2[i].id === item_1.id
}
});
Should be changed into this:
array_1.forEach(item_1 => {
for (let i = 0; i < array_2.length; i++) {
if (array_2[i].id === item_1.id) {
item_1.matches = true;
return;
}
}
item_1.matches = false;
});

Find index of a dynamic multidimensional array/json with matches id

thanks for checking,
I have a dynamic array which will contain multiple item/object.
I want the index number of this array, if a provided id matches with one of its contained
But Because it is a dynamically generated array/json
it can have any amount of multidimensional array inside child items and so on and so forth.
so is there any way to find the index number with matches id.
var data = [
{
id:1,
child:[
{
id: 2,
child: [
{
id: 3,
child: []
},
{
id:4,
child:[
{
id:44,
child:[
{
id:55,
child:[]
}
]
}
]
}
]
},
{
id:5,
child:[
{
id:6,
child:[]
}
]
}
]
}
]
Suppose i want to get the index of array where id is equal to 4.
I need to develop a logic/function which will return -> data[0]['child'][0]['child'][1]
Do it recursively
function findId(obj, id, currentPath = "") {
// Go through every object in the array
let i = 0;
for (let child of obj) {
// If id matches, return
if (child.id == id) return currentPath + `[${i}]`;
// Else go through the children, if we find anything there, return it
let next = findId(child.child, id, currentPath + `[${i}]['child']`);
if (next) return next;
i++;
}
// We didn't find anything
return null;
}
You could take a complete dynmaic approach with knowing some keys.
function findPath(object, id) {
var path;
if (!object || typeof object !== 'object') return;
if (object.id === id) return [];
Object.entries(object).some(([k, o]) => {
var temp;
if (temp = findPath(o, id, path = [])) {
path = [k, ...temp];
return true;
}
});
return path;
}
var data = [{ id: 1, child: [{ id: 2, child: [{ id: 3, child: [] }, { id: 4, child: [{ id: 44, child: [{ id: 55, child: [] }] }] }] }, { id: 5, child: [{ id: 6, child: [] }] }] }];
console.log(findPath(data, 44));
.as-console-wrapper { max-height: 100% !important; top: 0; }

add an object to an array if it doesn't have the same key with one of the objects in the array

I'm using Lodash. I have the array below:
const array = [{id:1,name:a},{id:2,name:b},{id:3,name:c},{id:4,name:d},{id:5,name:e}];
and I'm about to add another object to this array but before that, I need to check if the new object's name is already in the array or not and if there is one with the name I won't add the new object anymore.
I know some ways to do it, for instance, a loop with _.map, but want to make sure if there is an easier way.
You could use Lodash's some which if provided with an appropriate predicate e.g. (item => item.name === newName) will return a boolean indicating whether or not the item already exists (in this case, true would mean the name already exists). The benefit of using this over other iterating methods is that it will stop as soon as it finds one that returns true resulting in better performance.
With native javascript , you can use findIndex, this will return the index of the object where the name matches. If it returns -1 then there is no such object with same name. In that case update the array.
const array = [{
id: 1,
name: 'a'
}, {
id: 2,
name: 'b'
}, {
id: 3,
name: 'c'
}, {
id: 4,
name: 'd'
}, {
id: 5,
name: 'e'
}];
let newObjToAdd = {
id: 1,
name: 'z'
};
let newObjNotToAdd = {
id: 1,
name: 'a'
}
function updateArray(obj) {
let k = array.findIndex((item) => {
return item.name === obj.name;
})
if (k === -1) {
array.push(obj)
} else {
console.log('Array contains object with this name')
}
}
updateArray(newObjToAdd);
console.log(array)
updateArray(newObjNotToAdd);
You don't need lodash for some. You get that with native JS too (ES6):
const array = [{id:1,name:'a'},{id:2,name:'b'},{id:3,name:'c'},{id:4,name:'d'},{id:5,name:'e'}];
console.log(array.some(e => e.name === 'a'));
if (!array.some(e => e.name === 'z')) {
array.push({id: 5, name: 'z'});
}
console.log(array);
Doing this with lodash is few chars shorter but here is how you could do it with ES6 and Array.some:
const array = [{ id: 1, name: "A" }, { id: 2, name: "B" }, { id: 3, name: "C" }, { id: 4, name: "D" }, { id: 5, name: "C" }];
const maybeUpdate = (arr, obj) => {
if(!array.some(x => x.id == obj.id))
array.push(obj)
}
maybeUpdate(array, {id: 2, name: "F"}) // id exists wont insert
maybeUpdate(array, {id: 12, name: "F"}) // will insert
console.log(array)
Same idea with lodash and _.some would be:
const array = [{ id: 1, name: "A" }, { id: 2, name: "B" }, { id: 3, name: "C" }, { id: 4, name: "D" }, { id: 5, name: "C" }];
const maybeUpdate = (arr, obj) => {
if(!_.some(array, {id: obj.id}))
array.push(obj)
}
maybeUpdate(array, {id: 2, name: "F"}) // id exists wont insert
maybeUpdate(array, {id: 12, name: "F"}) // will insert
console.log(array)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Note that you could also use various other ways to get the same result. Array.find or _.find would work as well since all you have to do is to check if there was a hit:
const maybeUpdate = (arr, obj) => {
if(!_.find(array, {id: obj.id})) // or if(!array.find(x => x.id == obj.id))
array.push(obj)
}

Search deep in array and delete

I got the following array:
var arr = [
{
1: {
id: 1,
title: 'test'
},
children: [
{
1: {
id: 2,
title: 'test2'
}
}
]
}
];
The objects directly in the array are the groups. The 1: is the first language, 2: is second etc. The id is stored in every language object (due to the database I'm using). The children array is built the same way as the 'arr' array.
Example of multiple children:
var arr = [
{
1: {
id: 1,
title: 'test'
},
children: [
{
1: {
id: 2,
title: 'test2'
},
children: [
{
1: {
id: 3,
title: 'test3',
},
children: []
}
]
}
]
}
];
Now I need to delete items from this array. You can have unlimited children (I mean, children can have children who can have children etc.). I have a function which needs an ID parameter sent. My idea is to get the right object where the ID of language 1 is the id parameter. I got this:
function deleteFromArray(id)
{
var recursiveFunction = function (array)
{
for (var i = 0; i < array.length; i++)
{
var item = array[i];
if (item && Number(item[1].ID) === id)
{
delete item;
}
else if (item && Number(item[1].ID) !== id)
{
recursiveFunction(item.children);
}
}
};
recursiveFunction(arr);
}
However, I'm deleting the local variable item except for the item in the array. I don't know how I would fix this problem. I've been looking all over the internet but haven't found anything.
This proposal features a function for recursive call and Array.prototype.some() for the iteration and short circuit if the id is found. Then the array is with Array.prototype.splice() spliced.
var arr = [{ 1: { id: 1, title: 'test' }, children: [{ 1: { id: 2, title: 'test2' }, children: [{ 1: { id: 3, title: 'test3', }, children: [] }] }] }];
function splice(array, id) {
return array.some(function (a, i) {
if (a['1'].id === id) {
array.splice(i, 1)
return true;
}
if (Array.isArray(a.children)) {
return splice(a.children, id);
}
});
}
splice(arr, 2);
document.write('<pre>' + JSON.stringify(arr, 0, 4) + '</pre>');
var arr = [{ 1: { id: 1, title: 'test' }, children: [{ 1: { id: 2, title: 'test2' }, children: [{ 1: { id: 3, title: 'test3', }, children: [] }] }] }];
function deleteFromArray(id) {
function recursiveFunction(arr) {
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
if (item && Number(item[1].id) === id) {
arr.splice(i, 1);
} else if (item && Number(item[1].id) !== id) {
item.children && recursiveFunction(item.children);
}
}
};
recursiveFunction(arr);
};
deleteFromArray(2);
document.getElementById("output").innerHTML = JSON.stringify(arr, 0, 4);
<pre id="output"></pre>
jsfiddle: https://jsfiddle.net/x7mv5h4j/2/
deleteFromArray(2) will make children empty and deleteFromArray(1) will make arr empty itself.

JavaScript Object Array: Removing objects with duplicate properties

I have an array of objects:
[
{ id: 1, name: "Bob" },
{ id: 1, name: "Donald" },
{ id: 2, name: "Daryl" }
]
I'd like to strip out objects with duplicate Ids, leaving an array that would look like this:
[
{ id: 1, name: "Bob" },
{ id: 2, name: "Daryl" }
]
I don't care which objects are left, as long as each ID is unique. Anything in Underscore, maybe, that would do this?
Edit: This is not the same as the duplicate listed below; I'm not trying to filter duplicate OBJECTS, but objects that contain identical IDs. I've done this using Underscore - I'll post the answer shortly.
You can use reduce and some to good effect here:
var out = arr.reduce(function (p, c) {
// if the next object's id is not found in the output array
// push the object into the output array
if (!p.some(function (el) { return el.id === c.id; })) p.push(c);
return p;
}, []);
DEMO
the es6 way
function removeDuplicates(myArr, prop) {
return myArr.filter((obj, pos, arr) => {
return arr.map(mapObj => mapObj[prop]).indexOf(obj[prop]) === pos
})
}
Test it
let a =[
{ id: 1, name: "Bob" },
{ id: 1, name: "Donald" },
{ id: 2, name: "Daryl" }
]
console.log( removeDuplicates( a, 'id' ) )
//output [
{ id: 1, name: "Bob" },
{ id: 2, name: "Daryl" }
]
If you use underscore, you can use the _uniq method
var data = [
{ id: 1, name: "Bob" },
{ id: 1, name: "Donald" },
{ id: 2, name: "Daryl" }
]
_.uniq(data, function(d){ return d.ID });
Produces a duplicate-free version of the array, using === to test object equality. In particular only the first occurence of each value is kept. If you know in advance that the array is sorted, passing true for isSorted will run a much faster algorithm. If you want to compute unique items based on a transformation, pass an iteratee function.
Source: http://underscorejs.org/#uniq
Can use es6 Map collection mix with reduce
const items = [
{ id: 1, name: "Bob" },
{ id: 1, name: "Donald" },
{ id: 2, name: "Daryl" }
]
const uniqItems = [...items.reduce((itemsMap, item) =>
itemsMap.has(item.id) ? itemsMap : itemsMap.set(item.id, item)
, new Map()).values()]
console.log(uniqItems);
Using findIndex should be the simplest solution.
array.filter((elem, index, arr) => arr.findIndex(e => e.id === elem.id) === index)
You can simply filter the array, but you'll need an index of existing IDs that you've already used...
var ids = [];
var ar = [
{ id: 1, name: "Bob" },
{ id: 1, name: "Donald" },
{ id: 2, name: "Daryl" }
];
ar = ar.filter(function(o) {
if (ids.indexOf(o.id) !== -1) return false;
ids.push(o.id);
return true;
});
console.log(ar);
Here's some documentation on filter()...
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

Categories

Resources