I wanted to know what is the best way to extract the same named object from a multi level nested object.
I currently have an object that looks like this, and i want to extract out the parentLocationCluster objects out of it.
const foo = {
id: '1',
name: 'boo',
parentLocationCluster: {
id: 1,
name: 'foo',
parentLocationCluster: {
id: 2,
name: 'fii',
parentLocationCLuster: {
id: 3,
name: 'faa',
},
},
},
};
Now I could just a nested if statement like so:
const { parentLocationCluster } = foo;
if(parentLocationCluster) {
//do something
if(parentLocationCluster.parentLocationCluster) {
//do something
}
}
but i feel this is significantly inefficient ( which is what i am doing at the moment). Also, the objects could vary with the number of nested parentLocationCluster objects i.e. an object could contain 10 levels of parentLocationClusters.
What would be the best way to do this?
The following snippet recursively accesses all nested clusters to any depth and does something with them.
const foo = {
id: '1',
name: 'boo',
parentLocationCluster: {
id: 1,
name: 'foo',
parentLocationCluster: {
id: 2,
name: 'fii',
parentLocationCluster: {
id: 3,
name: 'faa',
},
},
},
};
function readCluster(obj) {
const cluster = obj.parentLocationCluster
if (cluster) {
// Do something
console.log(cluster.name)
readCluster(cluster)
} else
return;
}
readCluster(foo);
Related
I have an array of objects and Im trying to filter by matching ids
//Vehicle is added to quote
function filterByID(item) {
return item.id === 1;
}
this.vehicle = this.data.filter(filterByID);
data is as follows:
data: [
0: {
id: 0,
name: name
},
1: {
id: 1,
name: name
},
2: {
id: 2,
name: name
}
]
Im getting an empty error when I check the vehicle part
Are you using it like this:
const data = [
{
id: 0,
name: '',
},
{
id: 1,
name: '',
},
{
id: 2,
name: '',
},
];
function filterByID(item) {
return item.id === 1;
}
console.log(data.filter(filterByID)); // output: [{ "id": 1, "name": "" }]
You don't always need to define a separate function, you can use an arrow function, as below.
const data = [{
id: 0,
name: name
},
{
id: 1,
name: name
},
{
id: 2,
name: name
}
]
const vehicle = data.filter(item => item.id === 1);
console.log(vehicle);
This works fine in pure JS, it looks like it might be an issue with the lifecycle or state of your application. Use console.log to make sure that this.data is what you expect it to be
Yes I am aware of similar questions but it doesn't quite cover my use case here.
I'm having trouble finding a way to bring the time complexity of this down
I have two objects like so
const people = [
{
name: 'Steve',
id: 1,
fruitInBasket: 6
},
{
name: 'James',
id: 2,
fruitInBasket: 4
}
]
const homes = [
{
id: 1,
familyMembers: [
{
name: 'James',
id: 2
},
{
name: 'Steve',
id: 1
}
]
},
{
id: 2,
familyMembers: [
{
name: 'James',
id: 2
},
{
name: 'Steve',
id: 1
}
]
}
]
so one is a collection of people with a count of fruit in a basket and the other is a collection of homes and within each home is the same users as in the people collection.
Now I want to order the users in each home based on the count of fruitInBasket so I have done this
// create an empty table to store the order of the people
let orderTable = {};
// order the people based off the count in fruitInBasket using lodash orderBy
people = orderBy(people, ['fruitInBasket'], ['desc']);
// create the table
orderTable = people.reduce((acc, item, index) => {
return {
...acc,
[item.id]: index
}
}, {});
// order the people in each home based on the order in the `orderTable`
homes.forEach((home) => {
let members = [];
home.familyMembers.forEach((member) => {
let i = orderTable[member.id];
members[i] = member;
});
home.familyMembers = members;
})
so immediately you can see a nested for loop - which is never ideal.. but I can't figure out a way around this. This method has to sort through quite a lot of data and I am noticing huge performance issues.
Any help would be appreciated!
This should be O(N log N). Its performance bottleneck is the one time sort. Everything else is just O(N) iteration. Some microoptimizations still possible.
Generate an ordered map lookup table.
Just move arrays based on that mapping.
There is a hpc sort library that is measured around 10-40x faster than built-in JavaScript on benchmarks that can be added to increase performance.
I'm not sure, but is the familyMember table the same for every home object? Can you not just copy the same familyMember array to every object or do they have different properties?
Extra optimization per home could be to convert above table to index to index mapping, so that native level array indexing will be used for subsequent ordering.
const orderMap = Object.fromEntries(people.sort((x,y)=>x.fruitInBasket-y.fruitInBasket).map(({id},i)=>[id,i]))
// O(N)+O(NlogN)
homes.forEach(home=>{
const {familyMembers:fms} = home
const arr = new Array(fms.length)
//may want to prefill to maintain PACKED array: https://v8.dev/blog/elements-kinds#avoid-creating-holes
for(const fm of fms) arr[ orderMap[fm.id] ] = fm
home.familyMembers = arr
})
// map lookup O(N)
console.log(homes)
<script>
const people = [
{
name: 'Steve',
id: 1,
fruitInBasket: 6
},
{
name: 'James',
id: 2,
fruitInBasket: 9
}
]
const homes = [
{
id: 1,
familyMembers: [
{
name: 'James',
id: 2
},
{
name: 'Steve',
id: 1
}
]
},
{
id: 2,
familyMembers: [
{
name: 'James',
id: 2
},
{
name: 'Steve',
id: 1
}
]
}
]
</script>
You can filter and sort:
const people = [
{
name: 'Steve',
id: 1,
fruitInBasket: 6
},
{
name: 'James',
id: 2,
fruitInBasket: 4
},
{
name: 'Cesar',
id: 3,
fruitInBasket: 14
}
]
const homes = [
{
id: 1,
familyMembers: [
{
name: 'James',
id: 2
},
{
name: 'Cesar',
id: 3
},
{
name: 'Steve',
id: 1
}
]
},
{
id: 2,
familyMembers: [
{
name: 'James',
id: 2
},
{
name: 'Steve',
id: 1
}
]
}
]
homes.forEach(function(home){
home.familyMembers.sort((a,b)=>people.find(x=>x.id == a.id).fruitInBasket - people.find(x=>x.id == b.id).fruitInBasket)
})
console.log(homes)
Explanations:
You will iterate through homes:
homes.forEach(function(home){
You will sort the members:
.familyMembers.sort((a,b)
To sort, you have to get the fruits of the members, so you find the correct ID then take the correct property:
people.find(x=>x.id == a.id).fruitInBasket
Then you compare:
(a,b)=>people.find(x=>x.id == a.id).fruitInBasket - people.find(x=>x.id == b.id).fruitInBasket
If what you're looking for is performance, you should change people strucutre:
const people = {
1: {
name: 'Steve',
fruitInBasket: 6
},
2: {
name: 'James',
fruitInBasket: 4
}
}
This way you can retrieve fruits more easily:
people[id].fruits
Also, if your "id" is defined somewhere, don't define it in another place. Your homes should look like this:
const homes = {
1: {
familyMembers: [1, 2, 3]
},
2: {
familyMembers: [1,2]
}
}
So your algorithm goes like this:
const people = {
1: {
name: 'Steve',
fruitInBasket: 6
},
3: {
name: 'James',
fruitInBasket: 4
},
2: {
name: 'Cesar',
fruitInBasket: 9114
}
}
const homes = {
1: {
familyMembers: [1, 2, 3]
},
2: {
familyMembers: [1,2]
}
}
Object.keys(homes).forEach(function(k){
homes[k].familyMembers.sort((a,b)=>people[a].fruitInBasket - people[b].fruitInBasket)
})
console.log(homes)
For an object of this structure:
const myObj = {
id: 1,
name: '1',
children: [
{
id: 2,
name: '2',
children: [
{
id: 3,
name: '3',
children: []
}
]
},
{
id: 4,
name: '4',
children: [
{
id: 5,
name: '5',
children: [
{
id: 6,
name: '6',
children: [
{
id: 7,
name: '7',
children: []
}
]
}
]
}
]
},
]
}
How would I get an object by value (id)? So I'm looking for a function where if I call this:
findObj(myObj, 6);
It would return this:
{
id: 6,
name: '6',
children: [
{
id: 7,
name: '7',
children: []
}
]
}
Another example:
findObj(myObj, 3);
Would return this:
{
id: 3,
name: '3',
children: []
}
I know I need a recursion function. Heres what I have so far:
findObj(obj, id) {
if (obj.id === id) {
return obj;
}
obj.forEach(x => {
if (x.id === id) {
return x;
} else {
this.findObj(x.children, id);
}
});
}
(This is in an angular class)
At first sight I see some problems.
If you already have the ob.id === id inside the function, you don't need to double check for it later. Also, if findObj is a function defined in the scope like any other variable and is not in the "class" definition, you don't need to call it through this, the same way the first call to the findObj you do it without the this keyword.
Also, at first you pass an object to your method, but then you pass an object array (the obj.children), and also you check for the id inside a forEach method, where you pass a function (the x => { ... thingy), so when you return, you return from that function instead of the main function.
I've fixed it for you. I recommend you to understand what is happening and learn from it, as this will not happen if you know what you are doing (debug is your master here).
findObj(obj, id) {
if (obj.id === id) {
return obj;
}
for (var i = 0; i < obj.children.length; i++) { // No forEach, so when we return, we actually return from findObj
var r = findObj(obj.children[i], id); // We see if we have any return and let the "check logic" be defined on one location instead of duplicated.
if (r) {
return r;
}
}
}
You could check the object or check the children.
function find(object, id) {
var result;
if (object.id === id) return object;
object.children.some(o => result = find(o, id));
return result;
}
const object = { id: 1, name: '1', children: [{ id: 2, name: '2', children: [{ id: 3, name: '3', children: [] }] }, { id: 4, name: '4', children: [{ id: 5, name: '5', children: [{ id: 6, name: '6', children: [{ id: 7, name: '7', children: [] }] }] }] }] };
console.log(find(object, 5));
console.log(find(object, 6));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Two issues:
your return x; statement only returns from the forEach callback, not from the findObj function. Don't use forEach! A simple for … of loop will do
if your recursive call find the object and returns it, you ignore it and just continue iterating. Check whether you got a result and handle it appropriately.
I guess you're not looking for a full solution, so I leave that as an exercise :-)
I'm trying to figure out how to construct a hierachy object from some smaller objects that I have. Here's some example data:
{ id: 1, name: 'Jackson', parent: null },
{ id: 2, name: 'Jordan', parent: 1 },
{ id: 3, name: 'Jefferson', parent: 1 },
{ id: 4, name: 'Elvis', parent: 2 },
{ id: 5, name: 'Sally', parent: null },
{ id: 6, name: 'Eric', parent: 4 }
This would be constructed into a HIerarchy object that should look like so:
{
'1': {
name: 'Jackson',
children: {
'2': {
name: 'Jordan',
children: {
'4': {
name: 'Elvin',
children: {
'6': {
name: 'Eric',
children: { }
}
}
} },
'3': {
name: 'Jefferson',
children: { } }
}
},
'5': {
name: 'Sally',
children: { }
}
I'm really having a hard time figuring this out other then doing a for-loop for every id. (Ie: find all with null parent, find all with 1 parent, find all with 2 parent, etc...)
Here's my take on how to do this.
Create an object that will hold references to all the other objects, based on their key. This allows us to get a reference to each object by its key at the moment we need it as a parent of another element:
let register = {};
Create our output object:
let output = {};
Now let's go through each object in the array and add it to the structure:
// loop through the array
for (let el of elements) {
// clone the element, so we don't modify the original array
el = Object.assign({}, el);
// insert the clone into the register
register[el.id] = el;
if (!el.parent) { // if no parent is set, add it to the top level
output[el.id] = el;
} else { // otherwise, add it as a child of whatever the parent is
register[el.parent].children[el.id] = el;
}
// add a children property
el.children = {};
// remove the parent property
delete el.parent;
}
Remember that objects are always stored by reference, so modifying an object in the register object also modifies it in the output object.
Below is a working example.
let input = [{
id: 1,
name: 'Jackson',
parent: null
}, {
id: 2,
name: 'Jordan',
parent: 1
}, {
id: 3,
name: 'Jefferson',
parent: 1
}, {
id: 4,
name: 'Elvis',
parent: 2
}, {
id: 5,
name: 'Sally',
parent: null
}, {
id: 6,
name: 'Eric',
parent: 4
}];
let register = {};
let output = {};
// loop through the array
for (let el of input) {
// clone the element, so we don't modify the original array
el = Object.assign({}, el);
// insert the clone into the register
register[el.id] = el;
if (!el.parent) { // if no parent is set, add it to the top level
output[el.id] = el;
} else { // otherwise, add it as a child of whatever the parent is
register[el.parent].children[el.id] = el;
}
// add a children property
el.children = {};
// remove the parent property
delete el.parent;
}
console.log(JSON.stringify(output, undefined, 2));
Note that this will not work with circular references, or if the keys are out of order (i.e. the child appears before its parent).
I'm having some trouble understanding destructuring assignment in CoffeeScript. The documentation contains a couple of examples which together seem to imply that renaming objects during assignment can be used to project (i.e. map, translate, transform) a source object.
I am trying to project a = [ { Id: 1, Name: 'Foo' }, { Id: 2, Name: 'Bar' } ] into b = [ { x: 1 }, { x: 2 } ]. I've tried the following without success; I've clearly misunderstood something. Can anyone explain whether this is possible?
My Poor Attempts That Don't Return [ { x: 1 }, { x: 2 } ]
a = [ { Id: 1, Name: 'Foo' }, { Id: 2, Name: 'Bar' } ]
# Huh? This returns 1.
x = [ { Id } ] = a
# Boo! This returns [ { Id: 1, Name: 'Foo' }, { Id: 2, Name: 'Bar' } ]
y = [ { x: Id } ] = a
# Boo! This returns [ { Id: 1, Name: 'Foo' }, { Id: 2, Name: 'Bar' } ]
z = [ { Id: x } ] = a
CoffeeScript's Parallel Assignment Example
theBait = 1000
theSwitch = 0
[theBait, theSwitch] = [theSwitch, theBait]
I understand this example as implying that variables can be renamed which in this case is used to perform a swap.
CoffeeScript's Arbitrary Nesting Example
futurists =
sculptor: "Umberto Boccioni"
painter: "Vladimir Burliuk"
poet:
name: "F.T. Marinetti"
address: [
"Via Roma 42R"
"Bellagio, Italy 22021"
]
{poet: {name, address: [street, city]}} = futurists
I understand this example as defining a selection of properties from an arbitrary object which includes assigning the elements of an array to variables.
Update: Using thejh's Solution to Flatten an Array of Nested Objects
a = [
{ Id: 0, Name: { First: 'George', Last: 'Clinton' } },
{ Id: 1, Name: { First: 'Bill', Last: 'Bush' } },
]
# The old way I was doing it.
old_way = _.map a, x ->
{ Id: id, Name: { First: first, Last: last } } = x
{ id, first, last }
# Using thejh's solution...
new_way = ({id, first, last} for {Id: id, Name: {First: first, Last: last}} in a)
console.log new_way
b = ({x} for {Id: x} in a) works:
coffee> a = [ { Id: 1, Name: 'Foo' }, { Id: 2, Name: 'Bar' } ]
[ { Id: 1, Name: 'Foo' },
{ Id: 2, Name: 'Bar' } ]
coffee> b = ({x} for {Id: x} in a)
[ { x: 1 }, { x: 2 } ]
coffee>
CoffeeScript Cookbook solves exactly the same problem as yours - solution is map:
b = a.map (hash) -> { x: hash.id }
or list comprehension:
c = ({ x: hash.id } for hash in a)
You can check this fiddle online on CoffeScript homepage (uses console.info to show results).
EDIT:
To make it destrutive just assign mapped variable a to itself:
a = a1 = [ { Id: 1, Name: 'Foo' }, { Id: 2, Name: 'Bar' } ]
a = a.map (hash) -> { x: hash.Id }
console.info a;
a1 = ({ x: hash.Id } for hash in a1)
console.info a1;