I have been searching through the internet and cannot find the right word to search so I end up here asking again to all of you seniors in Javascript.
The code below is taken from the answer on these question
I really don't understand how the method or function work.
var list = [{id: 1,title: 'home',parent: null},{id: 2,title: 'about',parent: null},{id: 3,title: 'team',parent: 2},{id: 4,title: 'company',parent: 2} ];
function treeify(list) {
var treeList = [];
var lookup = {};
list.forEach(function(obj) {
obj['children'] = [];
lookup[obj['id']] = obj;
});
console.log(lookup); // [problem number 1]
list.forEach(function(obj) {
if (obj['parent'] != null) {
lookup[obj['parent']]['children'].push(obj);
} else {
treeList.push(obj);
}
});
console.log(treeList); // [problem number 2]
};
treeify(list);
On problem number 1:
It resulted an object that had already a children on each parent which is supposedly I think that, parent should have an empty array children at that moment. How does it work? Please enlightened me.
On problem number 2
treeList already formed the hierarchy tree. How it happen? it didn't even push the lookup variable to the treeList variable? It only pushes the obj with a parent that is equal to null (which is the root parent).
Hoping for your answer.
If you know any blog, article and etc. that may help me understand javascript please don't hesitate to add the link in your answer.
Many Thank you!
var list = [
{id: 1,title: 'home',parent: null},
{id: 2,title: 'about',parent: null},
{id: 3,title: 'team',parent: 2},
{id: 4,title: 'company',parent: 2}
];
treeify(list);
function treeify(list) {
var treeList = []; //local array
var lookup = {}; //local object
// iterate over each element in list array
list.forEach(function(obj) {
// add a children property to each element in the array
// in this case the children property is an array
obj['children'] = [];
// obj['id'] returns 1,2,3,4
// lookup is an object so we use the id of each element in the list array as a key
// first iteration adds key : 1 and value {id:1, title: 'home', parent: null, children: [] }
// second iteration adds key : 2 and value {id:2, title: 'about', parent: null, children: [] }
// ...
lookup[obj['id']] = obj;
});
/*
console.log(lookup) should output
{
1: {id: 1,title: 'home', parent: null, children: []},
2: {id: 2,title: 'about', parent: null, children: []},
3: {id: 3,title: 'team', parent: 2, children: []},
4: {id: 4,title: 'company', parent: 2, children: []}
}
however, if you run the code the lookup object gets modifyed in
the lines below (lookup[obj['parent']]['children'].push(obj);),
therefore, some items in the lookup object will have children elements in its child array
*/
console.log(lookup); // [problem number 1]
list.forEach(function(obj) {
if (obj['parent'] != null) {
// this line modifyes the lookup object at runtime
// obj has a parent, so we add it to the corresponding parents children array using the id
lookup[obj['parent']]['children'].push(obj);
} else {
//only adds an element to the treelist array if its a parent element
treeList.push(obj);
}
});
console.log(treeList);
};
Problem 1:
You are right with your assumption, but the problem is that console.log is logging a reference. Because the lookup object gets changed later on you see these changes. read more
function treeify(list) {
var treeList = [];
var lookup = {};
list.forEach(function(obj) {
obj['children'] = [];
lookup[obj['id']] = obj;
});
console.log(lookup); // Now you're right
};
Problem 2:
All objects in lookup are referenced to list.
lookup[obj['id']] = obj;
Here the childrens get changed.
lookup[obj['parent']]['children'].push(obj);
In treeList are also the same references.
Related
So I have a series of objects that are pulled from an API and inputted into an array, something like such:
array = [
{id: 0, name: "First", relationship: "Friend"},
{id: 1, name: "Second", relationship: "Friend"}
]
The user is allowed to add and remove objects to the list freely (they will appear within a Vue.JS DataTable), and said user is allowed a maximum of 4 objects within the array (lets say 4 "friends")
How should I go about implementing a function that searches the existing array (say, if its populated from the API), and inputs the new object with the corresponding ID that is missing (so if the user deletes the object with the id 2, and adds another, it will search said array with objects, find the missing id 2 slot in the array, and input the object in its place)?
Previously I have gone about it via implement array.find() with conditionals to see if the array contains or does not contain the certain id value, however, it searches through each entry and can end up inserting the same object multiple times. Another method I haven't attempted yet would be having a separate map that contains ids, and then when a user removes an object, having it correspond with the map, and vice versa when adding.
Any suggestions? Thanks
Instead of an array, I'd keep an object in data. Have it keyed by id, like this:
let objects = {
0: { id: 0, name: 'name0', relationship: 'relationship0' },
1: { id: 1, name: 'name1', relationship: 'relationship1' },
}
Integer keys in modern JS will preserve insertion order, so you can think of this object as ordered. The API probably returns an array, so do this...
// in the method that fetches from the api
let arrayFromApi = [...];
this.objects = array.reduce((acc, obj) => {
acc[obj.id] = obj; // insertion order will be preserved
return acc;
}, {});
Your UI probably wants an array, so do this (refer to "array" in the markup):
computed: {
array() {
return Object.values(this.objects);
},
To create a new object, insert it in order, minding the available keys. Note this is a linear search, but with small numbers of objects this will be plenty fast
methods: {
// assumes maxId is const like 4 (or 40, but maybe not 400)
createObject(name, relationship) {
let object = { name, relationship };
for (let i=0; i< maxId; i++) {
if (!this.objects[i]) {
object.id = i;
this.objects[i] = object;
break;
}
}
try this,
let array = [
{id: 0, name: "First", relationship: "Friend"},
{id: 4, name: "Second", relationship: "Friend"},
{id: 2, name: "Second", relationship: "Friend"},
]
const addItem = (item) => {
let prevId = -1
// this is unnecessary if your array is already sorted by id.
// in this example array ids are not sorted. e.g. 0, 4, 2
array.sort((a, b) => a.id - b.id)
//
array.forEach(ob => {
if(ob.id === prevId + 1) prevId++
else return;
})
item = {...item, id: prevId + 1 }
array.splice(prevId+1, 0, item)
}
addItem({name: "x", relationship: "y"})
addItem({name: "a", relationship: "b"})
addItem({name: "c", relationship: "d"})
console.log(array)
You can simply achieve this with the help of Array.find() method along with the Array.indexOf() and Array.splice().
Live Demo :
// Input array of objects (coming from API) and suppose user deleted 2nd id object from the array.
const arr = [
{id: 0, name: "First", relationship: "Friend" },
{id: 1, name: "Second", relationship: "Friend" },
{id: 3, name: "Fourth", relationship: "Friend" }
];
// find the objects next to missing object.
const res = arr.find((obj, index) => obj.id !== index);
// find the index where we have to input the new object.
const index = arr.indexOf(res);
// New object user want to insert
const newObj = {
id: index,
name: "Third",
relationship: "Friend"
}
// Insert the new object into an array at the missing position.
arr.splice(index, 0, newObj);
// Output
console.log(arr);
I have a nested array. Like below:
I want to find the depth of this nested array, which means the child element has most deep nested children.
let arr = [
{
name: 'tiger',
children: [{
name: 'sinba',
children: [{
name: 'cute',
children: []
}]
}]
},
{
name: 'lion',
children: []
}
]
In this case, the depth is 3, the tiger has 3 level. So the depth is 3
How could i achieve this? I try to use recursive, but don't know how to find the element which
has most nested children.
Thanks in advance.
Assuming that there are no circular references, you could try something like this
let arr = [{
name: 'tiger',
children: [{
name: 'sinba',
children: [{
name: 'cute',
children: []
}]
}]
},
{
name: 'lion',
children: []
}
]
function count(children) {
return children.reduce((depth, child) => {
return Math.max(depth, 1 + count(child.children)); // increment depth of children by 1, and compare it with accumulated depth of other children within the same element
}, 0); //default value 0 that's returned if there are no children
}
console.log(count(arr))
Our function would not work if there were some circular references, so there might be a need to adjust it accordingly. Detecting circular references is a whole ordeal. If nothing is done about it, the function will throw a Maximum call stack size exceeded error.
In order to handle it without any additional functionality implementation you could use already existing native JSON.stringify to do so. The stringify option will throw an exception only if you try to serialize BigInt values which we can handle ourselves or when objects are cyclic, which is excatly what we wanted.
let arr = [{
name: 'tiger',
children: []
}]
function testCircular(arr){
try {
BigInt.prototype.toJSON = function() { return this.toString() } // Instead of throwing, JSON.stringify of BigInt now produces a string
JSON.stringify(arr);
return false;
}
catch (e) {
// will only enter here in case of circular references
return true;
}
}
function count(children) {
if (testCircular(children)) return Infinity;
return children.reduce((depth, child) => {
return Math.max(depth, 1 + count(child.children)); // increment depth of children by 1, and compare it with accumulated depth of other children within the same element
}, 0); //default value 0 that's returned if there are no children
}
console.log(count(arr)) // normally counting
arr[0].children = arr; // creates circular reference
console.log(count(arr)) // counting for circular
I am trying to solve the problem in which i have to apply multiple filters to the array of object. Let suppose I am having a larger array of object which contains the configuration property which is further an object. On other side i have small object which are the ones the user chooses to filter(based on the checkboxes). i want to compare objects made with the parent array of objects by selecting multiple values.
So in the image the user chooses multiple values(using check boxes) and based on that he needs to filter the main array of objects.So after checking the checkboxes i get childObject and i have to filter parentArray on the basis of that..... please help me with this:
childobject =
{'Bathroom': '[2,1]',
'Bedroom': '[3,2]',
'halfBathroom':'0',
'name':'[2BD-2BA,2BD-2BA-1]'}
parentArray = [
0:{},
1:{},
2:{
'property1':'____',
'property2':'_____',
'configuration':'{
bathroom: 2
bedroom: 2
created_at: "2019-03-08 20:52:52"
created_by: 264
half_bathroom: 1
id: 26
is_selected: 0
name: "2BD-2BA-1/2BA"
name_en: "2BD-2BA-1/2BA"
name_es: "2RE-2BA-1/2BA"
status: 1
updated_at: "2019-08-23 05:39:44"
}'
}
3: {},
4:{}
]
I had to update the datastructure at some points:
You had different key in child and parent (upper/lowercase + camelcase/_ writing)
Some Missing } in the parent.
In child quotationmarks for integer deleted.Missing , added.
Changing some values in cruiteria, so that there is a result.
In parent delting of 0:, 1:, 2:, 3:, 4: to get a valid array.
childArray = {
'bathroom': [2,1],
'bedroom': [3,2],
'half_bathroom':1,
'name':['2BD-2BA', '2BD-2BA-1/2BA']
};
parentArray = [
{},
{},
{
'property1':'____',
'property2':'_____',
'configuration':{
bathroom: 2,
bedroom: 2,
created_at: "2019-03-08 20:52:52",
created_by: 264,
half_bathroom: 1,
id: 26,
is_selected: 0,
name: "2BD-2BA-1/2BA",
name_en: "2BD-2BA-1/2BA",
name_es: "2RE-2BA-1/2BA",
status: 1,
updated_at: "2019-08-23 05:39:44"
},
},
{},
{}
]
let res = parentArray.filter(elem => Object.entries(childArray).every(([key,val]) => {
let conf = elem.configuration;
if (conf===undefined) return false;
if (typeof(val) === 'object') {
return val.some(crit => crit===conf[key]);
} else {
return val===conf[key];
}
}));
console.log(res);
I'm building an array with data received from an API.
The result from the API contains a flat array of all pages on a site. I want to create a new array by making it multidimensional so that a page can have children, which can have their own children etc.
Expected array if I can get this to work:
array =
[{id:1, children:[] } // No children
,{id:2, children:[{id:3, children:[]}]} // One level of children
,{id:4, children:[{id:5, children:[{id:6, children:[]}]}]} // Two levels of children etc...
]
So what I'm doing now when looping through the API data is first checking if the element has children. If it doesn't have any I just append it to the array as it is but with an empty children[] as extra.
If the element has a parent I will get the parentId which I have to search for in my custom array.
if(!element.parent){
array.push(element);
continue;
}
// Now I know this element has a parent. So the fun begins.
var parentId = element.parent.id; // Here I have the parent ID
// Example of a none working append (because I dont know the depth)
for(var i = 0; i < array.length; i++){
if(array[i].id === parentId){
array[i].children.push(element);
}
}
So the question is how to loop through all these possible levels of children to find a match and append it correctly to my custom array?
Perhaps this code sample can lead you in the right direction:
var buildTree = function (arr, parent) {
var result = [];
var subTree = arr.filter((e) => e.parent === parent);
for (var i = 0; i < subTree.length; ++i) {
result[i] = {
...subTree[i],
children: buildTree(arr, subTree[i].id)
}
};
return result;
};
var arr = [{
id: 1,
parent: 0,
name: "item-1"
},
{
id: 2,
parent: 1,
name: "item-1.1"
},
{
id: 3,
parent: 2,
name: "item-1.1.1"
},
{
id: 4,
parent: 2,
name: "item-1.1.2"
},
{
id: 5,
parent: 1,
name: "item-1.2"
},
{
id: 6,
parent: 0,
name: "item-2"
},
];
var result = buildTree(arr, 0);
console.log(result);
The recursive buildTree method is returning each time the subtree from a given parent id (first time is expected to be the root of the tree).
BTW: in recursion, the most important thing you need to care about is, besides what you want your method to do (one level each time), the stop condition for your method (when you won't call it again). In this case when there are no more children (handled by the for loop over the filtered data).
I need to detect an object in an array with its Id.
My first array looks like that:
{ [id: 9, name: 'abc'], [id: 2, name 'def'], [id: 40, name: 'gh'] } (Id & name),
while that other array is:
{ [class: 'physics', Tid: 9], [class: 'computer science', Tid: 9], [class: 'Biology', Tid: 40] }.
I need to match the parameter "name" from the first array by its ID to its "class" (for example, "physics" relates to Tid=9 which is "abc" and "Biology" relates to Tid=40 which is "gh").
How can I elegantly do so without changing the way the data comes? (It comes from a database with ASP.NET web service in JSON)
You could use $http.get() which has success and error callback functions, which returns a promise object. Using this, you can setup a condition to map the id and get your desired result.
Something like this.
var myObject1 = {};
var myArray1 = [];
var myObject2 = {};
var myArray2 = [];
$http.get('json-file')
.success(function(data)) {
myObject1.myArray1 = data;
}
$http.get('json-file')
.success(function(data)) {
myObject2.myArray2 = data;
}
/* inside a loop if required */
if (myObject1.myArray1[index].id == myObject2.myArray2[index].Tid) {
/* perform logic */
}
This code would be present inside a service or a controller.
Haven't tested it so unsure of the syntax but promise objects are the way to go.
Hope this helps.
This returns an array of arrays. Each array in the array contains two objects matched by id === Tid. As far as I can tell that's what you want.
(Note that I think you provided broken sample arrays, I adjusted and scrambled the numbers around so you could see it work more clearly).
var arr1 = [ {id: 9, name: 'abc'}, {id: 2, name: 'def'}, {id: 40, name: 'gh'} ];
var arr2 = [ {class: 'physics', Tid: 2}, {class: 'computer science', Tid: 40}, {class: 'Biology', Tid: 9} ];
var arrFinal = arr1.map ( function ( d ) {
var matched = arr2.find ( function ( obj ) {
return obj.Tid === d.id;
} );
return [ d, matched ];
} );
If you iterate arrFinal you'll see it contains the matched objects.