How to add object properties together (number)? - javascript

Hey so I'm working on a JS project, and I came across an issue where I am trying to add/merge 2 objects together. So basically, there is a base object:
{age: 0,
lvl: 123,
xp: 321}
So we have this, and I have another object coming in with
{age: 12,
lvl: 21}
The result I want is
{age: 12,
lvl: 144,
xp: 321}
But that could be easily achieved with just individual property addition. However, I want to come to a point where I don't know what properties the object has, yet they are still added. Oh and the property type will for sure be a number. Any ideas?
Edit:
Ok, I see I mis worded some stuff. What I meant by me not knowing which properties it has, I meant that I know that the object may have one-all properties of the first object, just that I don't know which ones the second one has and does have.

Loop through the keys of the second object and add them to the first:
const first = {
age: 0,
lvl: 123,
xp: 321
};
const second = {
age: 12,
lvl: 21
};
for (const key in second) {
first[key] = (first[key] || 0) + second[key];
}
console.log(first);
Read more about for...in loops here.

Write a function that makes a copy of the first object and adds the keys in:
function addProperties(firstObj, secondObj) {
const newObj = Object.assign({}, firstObj);
for (let key of Object.keys(secondObj)) {
if (newObj.hasOwnProperty(key)) {
newObj[key] += secondObj[key];
} else {
newObj[key] = secondObj[key];
}
}
return newObj;
}
const first = {
age: 0,
lvl: 123,
xp: 321
};
const second = {
age: 12,
lvl: 21
};
const result = addProperties(first, second);
console.log(result);

Related

Understanding Behaviour of Find method in Javascript

let arr = [{ age: 3 }, { age: 5 }, { age: 6 }, { age: 7 }];
let exists = arr.find(x => x.age < 4);
exists.age += 1;
console.log(arr);
//output is [{age:4},{age:5},{age:6},{age:7}];
In the above example, I'm updating the result returned by the find method but it also changes the value of the original array why so?
It's because Objects in JavaScript are passed by reference, you got that object ( {age : 3} ) in exists then added 1 to it's "age" key , so the original object also changed .
let obj1 = {age: 3 , name: 'jack' }
let obj2 = obj1
console.log(obj1 === obj2 ) // true
// if you change the second object , the first one will change too :
obj2.age = 15
console.log(obj1 , obj2 )
// obj1 = { age: 15 , name: 'jack' }
// obj2 = { age: 15 , name: 'jack' }
Array.prototype.find will return the first element that satisfy the condition that you've passed as a callback function.
Since you are looking for an object whose age property is less than 4. So It will return first object(whose age is 3). So if you gonna check for equality of exists and arr[0] then It will return object that satisfy the condition
let arr = [{ age: 3 }, { age: 5 }, { age: 6 }, { age: 7 }];
let exists = arr.find((x) => x.age < 4);
console.log(exists === arr[0])
So, If you are going to do any kind of mutation with the object that is returned by the find method then the original object will reflect the changes.
Because both are same object just different references.
If you don't want to mutate the original object then you should clone it before doing any kind of changes to that object.
Note: Both of the following method does shallow copy
1) Using spread syntax
let arr = [{ age: 3 }, { age: 5 }, { age: 6 }, { age: 7 }];
let exists = arr.find((x) => x.age < 4);
const clone = { ...exists };
clone.age += 1;
console.log(arr);
/* This is not a part of answer. It is just to give the output full height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
2) Using Object.assign
let arr = [{ age: 3 }, { age: 5 }, { age: 6 }, { age: 7 }];
let exists = arr.find((x) => x.age < 4);
const clone = Object.assign({}, exists);
clone.age += 1;
console.log(arr);
Because you have an array of object references. In JavaScript, objects are actually stored elsewhere (called the "heap") and object variables actually only contain the references to object. So the reason is because you're updating the same object.
If you want to do a shallow clone of an object, you can use Object.assign({}, obj).
Further, not directly relevant to your question, if your object properties themselves were to contain other object references, including arrays, and you want those to be copies as well, you'll have to deep-clone them. This is not automatically done by a stock JavaScript function or method. You'll have to find code that does that for you or write it yourself. Last time I did this, I used randa's clone function because a different developer on my team had already imported the ramda library into our project. What makes the most sense for your project may be different.

Why DOM element array can be indexOF?

I know indexOf cannot be used in an array of objects, e.g. below.
const objs = [
{name: 'darling', age: 28},
{name: 'eliot', age: 29}
]
console.log(objs.indexOf({ name: 'eliot', age: 29 })) // print -1
However, why DOM element array index can be traced by indexOf??
e.g. below
document.getElementById('slides').addEventListener('click', function (e) {
const nodes = document.querySelectorAll('#slides > .slide')
const newNodes = [...nodes]
console.log(newNodes);
console.log(newNodes.indexOf(e.target)) // can print out the index
})
Every time the interpreter comes across an object literal when it runs a line - for example:
const objs = [
{name: 'darling', age: 28},
{name: 'eliot', age: 29} // this line
]
or
console.log(objs.indexOf(
{ name: 'eliot', age: 29 } // this line
))
It creates a new object with those properties. They aren't the same.
console.log(
// two separate objects, which happen to contain the same value
{ foo: 'foo' } === { foo: 'foo' }
);
In contrast, when looking up nodes in the DOM, the nodes are generally static - they don't change unless JavaScript runs which explicitly does so. The e.target refers to one such static node, and querySelectorAll returns a collection of such static nodes.
So they're ===, and .indexOf works.
Consider the following behavior:
const element = { foo: 1 };
const arr = [{ foo: 1 }, { foo: 2 }, element];
console.log(arr.indexOf({ foo: 1 })); // -1
console.log(arr.indexOf(element)); // 2
console.log(arr.indexOf(arr[0])); // 0
It clearly demonstrates how indexOf matches the reference to an object, rather than an object with the same keys and values.
For one { name: 'eliot', age: 29 } is a new object and hence not found in your array
So you can find an object if it is the actual same object
Otherwise look for it by the value of one or more of the entries
const objs = [
{name: 'darling', age: 28},
{name: 'eliot', age: 29}
]
const objToFind = objs[1]
console.log(objs.indexOf(objToFind))
// find the index by name
const idxOfEliot = objs.findIndex(({name}) => name==="eliot")
console.log(idxOfEliot)

How can I know how many matches I have between two arrays (one nested inside an object)?

I need to know how many matches I have between people.compare array and names array, then create a new object including this data.
names = [ 'Juan', 'Old']
people = [
{compare: ['Juan', 'Old'], Age: 70},
{compare: ['Juan', 'Young'], Age: 20}
]
Expected output:
peopleNew = [
{compare: ['Juan', 'Old'], 'Age': 70, 'MATCHES': 2},
{compare: ['Juan', 'Young'], 'Age': 20, 'MATCHES': 1}
]
This code loops through each array and uses Array.includes() to check each item for equality:
const peopleNew = people.map(obj => {
let matches = 0;
obj.compare.forEach(name => {
if (names.includes(name)) {
matches++; // Increase count by 1
}
})
// Create new object from original with new 'matches' property
return Object.assign({}, obj, { matches });
});

JavaScript: How to copying a few keys from a nested object by destructuring

Say I have an object:
myObj = {
name: 'Luke',
age: 12,
height: '163cm',
weight: '60kg',
others: { one: '1', two: '2', three: '3'} // (Edited) Added one more key here :)
};
I want a copy of this object without certain keys to a new object in a way that the output is as below:
newObj = {
name: 'Luke',
age: 12,
one: '1',
two: '2'
};
I have seen examples of destructing but I wanted to know if it is possible with nested objects. Is something like this doable using destructuring or if not what would be the most efficient way to do this.
One way to achieve this with destructure-like syntax would be like this:
const myObj = {
name: 'Luke',
age: 12,
height: '163cm',
weight: '60kg',
others: { one: '1', two: '2', three : '3'}
};
const newObj = {
/* Copy over values from "myObj" to equivalent keys in "newObj" */
name : myObj.name,
age : myObj.age,
/* Spread keys "one" and "two" of the nested "others" object into "newObj" */
...({one, two} = myObj.others, {one, two})
}
console.log(newObj)
For completeness, an iife approach is possible. It works by creating an arrow function that takes as parameters keys you want to keep. In the function body, spread nested objects as desired.
const myObj = {
name: 'Luke',
age: 12,
height: '163cm',
weight: '60kg',
others: { one: '1', two: '2'}
};
const newObj = (
({name, age, others}) => ({name, age, ...others})
)(myObj);
console.log(newObj);
For the object of unknown depth you can try using recursion.
Create a function which takes an object and array of keys to be removed as arguments.
Create a helper(which takes 1 object as parameter) function inside main and create empty object.
Loop through the properties of obj using for..in.
check if key is not present in the array of keys to be removed then
Check if the value is object then call the function recursively
If its not object then add it to the result obj.
At last return the result object.
The code will convert the object of unknown depth to a plain object and you can also remove keys from nested objects.
const myObj = {
name: 'Luke',
age: 12,
height: '163cm',
weight: '60kg',
others: { one: '1', two: '2'}
};
const removed = ['height','weight','one'];
function removeKeys(obj,removed){
const res = {};
function helper(obj){
for(let key in obj){
if(!removed.includes(key)){
if(typeof obj[key] === "object"){
helper(obj[key]);
}
else res[key] = obj[key]
}
}
}
helper(obj)
return res;
}
const res = removeKeys(myObj,removed);
console.log(res)

JavaScript Syntax Explanation - return { ...state}; [duplicate]

I have a problem with code I am supposed to work with. I found a syntax I am not familiar with and I have trouble googling the documentation:
export const Something = class Something {
constructor(someObject = {}) {
this.someObject = {...Something.someObjectDefaultAsStaticMethod,...someThing};
};
// The rest of the class
};
I have problems understanding what the three dots (...) in front of the parameter do. And "dots in parameter javascript" is a bad search term. Can someone help me, maybe tell me what this syntax is actually called or just directly link me to documentation?
That is not ES6 but has only been added in ECMAScript 2018.
It is called "Object Rest/Spread Properties" and is part of the Spread Syntax.
... (three dots in Javascript) is called the Spread Syntax or Spread Operator. This allows an iterable such as an array expression or string to be expanded or an object expression to be expanded wherever placed.
I want to list down the mostly used practical Use Cases of the Spread Syntax (Spread Operator). The following has been explained with examples in this stackoverflow answer.
Combine Arrays (Concatenate Arrays)
Copying Arrays
Calling Functions without Apply
Destructuring Arrays
Function Arguments as Rest Parameters
Using Math Functions
Combining Two Objects
Separate a String into Separate Characters
The [...something] is the spread operator. It in essence allows for an array or string to be expanded. You will see it used often in React, but has many other use cases.
MDN has great documentation on the spread operator:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator
You can use "..." in an object. In this example below, "...data" gets 'name: "John", age: 24':
const data= { name: "John", age: 24 };
const newData = {
...data, // Here
sex: "Male"
}
console.log(newData);
This is the result:
{ name: "John", age: 24, sex: "Male" }
This is other example with "...data[key]" to add "id" to each object in an array:
const data = [
{ name: "John", age: 24 },
{ name: "Marry", age: 18 },
{ name: "Tom", age: 15 },
]
const newData = [];
for(const key in data) {
const obj = {
id: Number(key),
...data[key] // Here
}
newData.push(obj);
}
console.log(newData);
This is the result:
[
{ id: 0, name: "John", age: 24 },
{ id: 1, name: 'Marry', age: 18 },
{ id: 2, name: 'Tom', age: 15 }
]
Context: One of the use cases is to do a "copy", but you should take care of this particular "by reference" behavior when working with sub-properties.
Finding: Take care that sub-properties are NOT passed by value, but by reference. In other words, only first level properties are passed as a copy "by value". See the example:
sourcePerson = { name: 'John', colors: ["red", "blue"] }
targetPerson = { ...sourcePerson }
console.log("Target person result:\n", JSON.stringify(targetPerson), "\n\n") //it seems a copy, but...
console.log("Let's update the name source value:\n")
sourcePerson.name = 'Kevin'
console.log("Updated source person:\n", JSON.stringify(sourcePerson), "\n")
console.log("Target person is NOT updated, It keeps a copy by value\n")
console.log(JSON.stringify(targetPerson), "\n\n")
//But if you update a sub-property, it has NOT been copied by value
console.log("Let's update a color sub-property:\n")
sourcePerson.colors[0] = "YELLOW"
console.log("Updated source person:\n", JSON.stringify(sourcePerson), "\n")
console.log("Target person is updated BY REFERENCE!\n")
console.log(JSON.stringify(targetPerson)) // it is not a copy, it is a reference!
console.log("\nCONCLUSION: ... spread syntax make a copy 'by value' for first level properties, but 'by reference' for sub-properties, so take care!\n")

Categories