Individual property update vs object re assigning in angular - javascript

I have an object like this.
steps:any = [
{
id: 1, name: "A", next: [{ id: 2, name: "B" }, { id: 3, name: "C" }]
},
{
id: 2, name: "B", next: [{ id: 1, name: "B" }]
},
{
id: 3, name: "C", next: [{ id: 1, name: "B" }]
},
{
id: 4, name: "D", next: [{ id: 1, name: "B" }]
}
]
and on HTML
<div *ngFor="let step of steps">
{{step.name}}
<div *ngFor="let nextStep of step.next">
<span (click)="change(step,nextStep)">{{nextStep.name}}</span>
</div>
</div>
and ts
change(step, nextStep) {
let __step = this.steps.find((_step) => {
return _step.id === step.id;
});
let __nextStep = __step.next.find((_nextStep) => {
return _nextStep.id === nextStep.id;
});
__nextStep = {name:"XYZ",id:"10",next:[]}
}
According to this the clicked object must change its value? What this is not working, the name remains the same, but if I do
__nextStep.name ="XYZ";
it works, but if assign the object not working. Am I missing something?

__nextStep is just a local variable that initially holds a reference to an element of steps. When you reassign it with:
__nextStep = {name:"XYZ",id:"10",next:[]};
you're not doing anything to the original object. Assigning to a variable has no effect on the object that the variable previously referred to.
You can use Object.assign() to replace the properties of an existing object:
Object.assign(__nextStep, {name:"XYZ",id:"10",next:[]});

That's because your object is a new memory reference.
When you write
__nextStep = {name:"XYZ",id:"10",next:[]}
Your create a new memory reference, which has nothing to do with the previous value, and Angular loops are looking for changes inside memory references you provide at start.
This means that since your new memory reference is out of the array, Angular doesn't change it.
If you want to be able to assign new memory references and still check for changes, you'll have to write a custom trackby function that tracks by name.
<div *ngFor="let step of steps; trackBy: customTB">
customTB(index, item) {
return `${index}-${item.name}$`;
}
You can also try with
<div *ngFor="let step of steps; trackBy: step.name">
But i've never tested it myself.

You are updating only the local value to update the value you need to find the index from the array like this
change(step, nextStep) {
let __stepIndex = this.steps.findIndex((_step) => {
_step.id === step.id;
});
let __nextStepIndex =this.steps[__stepIndex].next.findIndex((_nextStep) => {
_nextStep.id === nextStep.id;
});
__nextStep = {name:"XYZ",id:"10",next:[]};
this.steps[__stepIndex].next[__nextStepIndex]=__nextStep;
}

Related

Javascript : Modify object and add new index to a property

My object is something like:
let items =
[
{
"creationTimeStamp": "2022-05-31T17:04:28.000Z",
"modifiedTimeStamp": "2022-05-31T17:04:28.000Z",
"locations": [
{
"id": "5ao",
"name": "Store1"
}
],
"typeId": "Lead"
}
]
I am trying to push the following object into the locations property:
{
"id": "9a0",
"name": "Store2"
}
I have tried doing
items1 = [];
for (var i = 0; i < items.length; i++) {
items1.id = "9a0";
items1.name = "Store2";
//9 is some static index value added
Object.assign({9 : items1}, items[i].locations);
}
If I console(Object.assign({9 : items1}, items[i].locations)); I can see 2 arrays inside it, but my items locations property is still the same.
My expectation is as below:
[
{
"creationTimeStamp": "2022-05-31T17:04:28.000Z",
"modifiedTimeStamp": "2022-05-31T17:04:28.000Z",
"locations": [
{
"id": "5ao",
"name": "Store1"
},
{
"id": "9a0",
"name": "Store2"
}
],
"typeId": "Lead"
}
]
I also tried to use items[i].locations.push(item1) but then got:
TypeError: Cannot add property 9, object is not extensible
I also tried to assign a new array to items[i].locations, but then got:
TypeError: Cannot assign to read only property 'locations' of object '#'
What can I do to get the desired result?
You seem to expect that the second argument given to Object.assign will be mutated. But it is the first argument that is mutated. That means your .locations is not mutated. Moreover, in comments you indicate that locations cannot be extended and that the property is read-only.
So that means you'll need a complete new object.
Some other remarks:
Don't initialise items1 as an array, since it is supposed to be a plain object.
Declare a variable with const, let or var and avoid implicit global declaration.
It is safer to declare the items1 object inside the loop, so you create a new object each time and don't mutate the same object. For your example code it makes no difference, but it can lead to unexpected behaviour.
As you don't need i for anything else than items[i], and you actually need a complete new structure, use .map instead.
So:
items = items.map(item => {
let obj = {
id: "9a0",
name: "Store2"
};
return {...item, locations: item.locations.concat(obj) };
});
I always think in terms of functions, and of immutability-by-default, so my approach might look like this, with addLocationToAll built atop a simpler addLocation. The code is fairly simple:
const addLocation = (newLoc) => ({locations, ...rest}) =>
({...rest, locations: locations .concat (newLoc)})
const addLocationToAll = (newLoc) => (items) =>
items .map (addLocation (newLoc))
const items = [{creationTimeStamp: "2022-05-31T17:04:28.000Z", modifiedTimeStamp: "2022-05-31T17:04:28.000Z", locations: [{id: "5ao", name: "Store1"}], typeId:"Lead"}]
const newLoc = {id: "9a0", name: "Store2"}
console .log (addLocationToAll (newLoc) (items))
.as-console-wrapper {max-height: 100% !important; top: 0}
items is an array so it must access the first position of the array, which would be the proposed object.
With this, from the proposed object you will extract thelocation attribute and since this is an array, you use the push function to insert the new object
items[0]
// ->
// {
// creationTimeStamp: '2022-05-31T17:04:28.000Z',
// modifiedTimeStamp: '2022-05-31T17:04:28.000Z',
// locations: [ { id: '5ao', name: 'Store1' } ],
// typeId: 'Lead'
// }
I try this:
items[0].locations.push({"id": "9a0", "name": "Store2" })
And now:
items[0]
//->
// {
// creationTimeStamp: '2022-05-31T17:04:28.000Z',
// modifiedTimeStamp: '2022-05-31T17:04:28.000Z',
// locations: [ { id: '5ao', name: 'Store1' }, { id: '9a0', name: 'Store2' }],
// typeId: 'Lead'
// }

Find deep nested array depth

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

How to add a new object to an array of objects that is nested inside an array of objects in ReactJS?

I am new to ReactJS and JS and always get stuck with updating the state using object spread.
I have simplified my data as follows and consider the 0th index for the state for my example,
state = [
{
id: "1",
name: "Some name",
someNestedArrayProperty: [
{
id: "1",
name: "Name 1"
},
{
id: "2",
name: "Name 2"
}
]
},
{...}
]
I want to add a newObject into someNestedArrayProperty that I receive from action creator, say
newObject = {
id: "3",
name: "Name 3"
}
I am not getting the correct syntax. I tried the following and it is messing up the state.
Below is the simplified code of what I am trying to do in my application,
let someNestedArrayProperty= [
...state[0].someNestedArrayProperty,
action.someNestedArrayProperty
];
return [
{
...state,
[0]: { //I guess the problem is in this line "[0]", but I am not getting the correct syntax
...state[0],
someNestedArrayProperty: someNestedArrayProperty
}
}
];
Note: I am returning the modified state from the Redux reducer
You can use map function to modify your state like following:
const newState = state.map((s, idx) => {
// Modification needed elemet
if (idx === 0) {
return {
...s,
someNestedArrayProperty: someNestedArrayProperty
}
}
// Modification not needed
else {
return s;
}
})

Objects inside Array, how to solve this problem?

My question is, how can i access each object inside an Array? or
perhaps, how am I supposed to solve this problem? Like I know in head,
i have to compare categories and then push into new const array that
category. So far, i get each object in array wrote down, but I need to
do an push method after category is same, and also after that to
splice the category from each object.
My solution so far:
export const convert = inside => {
inside(({id,name,category}) => {
outside[category].push({id,name});
});
console.log(outside);
return outside;
}
Sorry for messed code, could not load here.
You could take the category as key for the object and push a new object.
No need for an array for every category, because this appoach uses the result object with dynamic keys.
const
inside = [{ id: 1, name: "orange", category: "fruits" }, { id: 2, name: "apple", category: "fruits" }, { id: 3, name: "carrot", category: "vegetable" }],
outside = {};
inside.forEach(({ id, name, category }) => {
outside[category] = outside[category] || [];
outside[category].push({ id, name });
});
console.log(outside);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I'm not fully sure i understand the question but from what i think is needed, you want to take all the items from the outside constant and take their respective category, apply it to the food Object and then add that Object to the inside variable.
const outside = {
fruits: [{
id: 1,
name: "orange"
}, {
id: 2,
name: "apple"
}],
vegetable: [{
id: 3,
name: "carrot"
}]
}
const categories = Object.keys(outside)
let inside = []
categories.forEach(category => {
const categorizedFood = outside[category].map(f => ({...f, category }) )
inside = [...inside, ...categorizedFood]
})
console.log(inside)
.as-console-wrapper {
background: #FFF;
filter: invert(1) hue-rotate(210deg);
}

JavaScript - find an object by ID in array and modify

Please note that the following scenario is for the demonstration purposes only.
Lets assume I have a following array of object:
var obj = [{
id: 4345345345,
cat: [{
id: 1,
cat: "test1"
}, {
id: 2,
cat: "test2"
}]
}, {
id: 3453453421,
cat: [{
id: 1,
}, {
id: 2,
}]
}];
My goal is to :
Find an object within an array with #id 4345345345, add property selected : true to it
Then within this object with #id 4345345345, find cat with #id 2, add property
selected : true to it
The below works, however should my array have 1000+ objects it's feels somehow wasteful, can you please suggest any cleaner/clever solution ( possible using underscore)?
for (var i = 0; i < obj.length; i++) {
var parent = obj[i];
if (parent.id === 4345345345) {
parent.selected = true;
for (var j = 0; j < parent.cat.length; j++) {
var sub = parent.cat[j];
if(sub.id === 2) {
sub.selected = true;
}
};
}
};
Here are a few approaches I can think of
1) change your data structure to use the id's as the key. ie.
4345345345 :
cat: { 1 :{
cat: "test1"
}, 2 : {
cat: "test2"
}}
2) Alternatively you can create a temporary lookup table based on the id to directly look the object actual objects; obviously you would only create the look up table one time, or whenever the data changes. This will bring your runtime from O(n) to O(1).

Categories