i want to push items from one array to another but i'm getting only duplicates of last item. is there a way or better way of working this out? thanks
let nums = [{id:1, first_name: "sade", last_name: "Smith"}, {id:2, first_name: "Jon", last_name: "Doe"}];
let em = [];
let num2 = {id:null, name: ""}
nums.forEach(e => {
num2.id = e.id;
num2.name = `${e.first_name} ${e.last_name}`;
em.push(num2)
});
console.log(em)
As mentioned in the other answers, the issue is that you're changing an existing object every iteration instead of adding a new one.
I'd like to add that that you can also use reduce instead (I personally prefer the syntax and it's a bit shorter):
let nums = [{id:1, first_name: "sade", last_name: "Smith"}, {id:2, first_name: "Jon", last_name: "Doe"}];
let em = [];
let num2 = {id:null, name: ""}
em = nums.reduce((accumulator, item) => {
accumulator.push({
id: item.id,
name: `${item.first_name} ${item.last_name}`;
})
}, [])
More info about the reduce function can be found in the MDN docs.
Why does it work like this?
If you want to know more about why your solution didn't work, it's important to understand that in JS there's a difference between primitive values and objects.
By assigning a variable that contains an object to another variable, you are not assigning a new object, but only pointing to an existing one instead. This causes coupling between your objects, they are pointing to the same object, as can be demonstrated here:
var obj1 = {name: 'Sherlock Holmes', country: 'England'}
var obj2 = obj1;
obj2.country = 'US';
console.log(obj1.country) // US
console.log(obj2.country) // US
Because of this, when you want to assign an object to another variable you need to clone its content, but create a new object, therefor the variable will point to a new object and not to the old one.
There are many ways to do it, but few simple ones are:
Using the spread operator like this:
var obj1 = {name: 'Sherlock Holmes', country: 'England'}
var obj2 = { ...obj1 };
obj2.country = 'US';
console.log(obj1.country) // England
console.log(obj2.country) // US
Stringifying then parsing using JSON.stringify and JSON.parse
var obj1 = {name: 'Sherlock Holmes', country: 'England'}
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.country = 'US';
console.log(obj1.country) // US
console.log(obj2.country) // US
There are more ways, and differences between those ways, if your object has nested objects as well, the replications might not occur correctly, at this point it's best to use a function that does the cloning for you, like lodash's deepClone function.
Just clone num2 and push the cloned object.
let nums = [{id:1, first_name: "sade", last_name: "Smith"}, {id:2, first_name: "Jon", last_name: "Doe"}];
let em = [];
let num2 = {id:null, name: ""}
nums.forEach(e => {
const cloned = {...num2};
cloned.id = e.id;
cloned.name = `${e.first_name} ${e.last_name}`;
em.push(cloned)
});
console.log(em)
Your issue is that each time, you're adding the same object to the list, num2. num2 itself is not changing through each iteration of the loop.
This is an effect of you never reassigning num2 to anything new, and the fact that the list nums is keeping track of a list of pointers to objects, which means it doesn't actually copy num2 into itself each time, it just puts num2's pointer in.
You should either 1) clone it or just 2) make a new object then push it to the list.
let nums = [{id:1, first_name: "sade", last_name: "Smith"}, {id:2, first_name: "Jon", last_name: "Doe"}];
let em = [];
let num2 = {id:null, name: ""}
nums.forEach(e => {
let temp_num = {...num2};
temp_num.id = e.id;
temp_num.name = `${e.first_name} ${e.last_name}`;
em.push(temp_num)
});
console.log(em)
It is a fairly simple map() operation to create the new array from the original
let nums = [{id:1, first_name: "sade", last_name: "Smith"}, {id:2, first_name: "Jon", last_name: "Doe"}];
let res = nums.map(({id, first_name:fn, last_name:ln}) => ({id, name: `${fn} ${ln}`}));
console.log(res)
That's because you keep pushing the same object to the em array in each iteration in the forEach loop.
A simple approach would be to do this:
const nums = [{id:1, first_name: "sade", last_name: "Smith"}, {id:2, first_name: "Jon", last_name: "Doe"}];
const em = [];
nums.forEach(e => {
em.push({id: e.id, name: `${e.first_name} ${e.last_name}`});
});
console.log(em);
You could also use the map functionality on arrays:
const nums = [{id:1, first_name: "sade", last_name: "Smith"}, {id:2, first_name: "Jon", last_name: "Doe"}];
const em = nums.map((item) => ({id: item.id, name: `${item.first_name} ${item.last_name}`}));
console.log(em);
Essentially, we are creating a new object and pushing it into the em array in every iteration in the loop and in the process, removing the need for the num2 object that you've declared.
In addition, the em variable can be made const since using a const variable does not mean that the value it holds is immutable; we're simply pushing new elements into the array which is perfectly possible.
Related
I have a object which has some properties for one user, and I have array of objects which is returned from API.
My goal is to check which object of Array of objects has the same property as the one single initial object, and then it should return only part of it's properities.
I have tried to use .map on Array of objects but it seems not workig.
Below is the code example. I have also prepared codesandbox if You wish.
const user =
{
name: "jan",
lastName: "kowalski",
fullName: "jan kowalski",
car: "audi"
}
;
const usersAnimal = [
{
name: "jan",
lastName: "kowalski",
fullName: "jan kowalski",
animal: "cat",
animalSize: "small",
animalName: "Bat"
},
{
name: "john",
lastName: "smith",
fullName: "john smith",
animal: "dog",
animalSize: "middle",
animalName: "Jerry"
},
{
name: "Anna",
lastName: "Nilsson",
fullName: "Anna Nilsson",
animal: "cow",
animalSize: "big",
animalName: "Dorrie"
}
];
const filtered = usersAnimal.map((userAnimal)=>userAnimal.fullName === user.fullName && return userAnimal.animalName & userAnimal.animalSize & userAnimal.animal);
thanks
https://codesandbox.io/s/admiring-edison-qxff42?file=/src/App.js
For case like this, it would be far easier if you filter it out first then proceed using map:
const filtered = usersAnimal
.filter((animal) => animal.fullName === user.fullName)
.map(({ animalName, animalSize, animal }) => {
return {
animalName,
animalSize,
animal
};
});
I am providing a for loop solution as I haven't learnt many array methods in javascript.
For me the simplest option is to use a for loop and an if check to loop through the arrays values to check for included values.
for (let v in usersAnimal) {
if (usersAnimal[v].fullName === user.fullName) {
console.log(usersAnimal[v])
}
}
The code above will log the entire usersAnimal object containing the fullname we are looking for.
{
name: 'jan',
lastName: 'kowalski',
fullName: 'jan kowalski',
animal: 'cat',
animalSize: 'small',
animalName: 'Bat'
}
commented for further understanding
for (let v in usersAnimal) {
//loops though the array
if (usersAnimal[v].fullName === user.fullName) {
//when the index value 'v' has a fullname that matches the user fullname value
// it passes the if check and logs that object value
return console.log(usersAnimal[v])
//return true...
}
//return null
}
//etc
If you want to filter, I recommend you to use filter.
The map method will create a new array, the content of which is the set of results returned by each element of the original array after the callback function is operated
const user = {name:"jan",lastName:"kowalski",fullName:"jan kowalski",car:"audi"};
const usersAnimal = [{name:"jan",lastName:"kowalski",fullName:"jan kowalski",animal:"cat",animalSize:"small",animalName:"Bat"},{name:"john",lastName:"smith",fullName:"john smith",animal:"dog",animalSize:"middle",animalName:"Jerry"}];
// Get an array of matching objects
let filtered =
usersAnimal.filter(o => o.fullName === user.fullName);
// You get the filtered array, then you can get the required properties
filtered.forEach(o => {
console.log(
'animal:%s, animalSize:%s, animalName:%s',
o?.animal, o?.animalSize, o?.animalName
);
});
// Then use map to process each element
filtered = filtered.map(o => {
const {animal, animalSize, animalName} = o;
return {animal, animalSize, animalName};
});
console.log('filtered', filtered);
I have an array of objects, that contains properties that are objects:
let allPersons = [
{ id: "abcdefg",
name: "tom",
...
phone: {
brand: "blah"
id: "hijklm"
...
}
},
{ id: ....}, {...}, {...}
];
What I need to do is filter those objects and returning all the phones, filtering them by id so all phones returned are unique.
I tried to retrieve first all the phones:
// allPersons is the full array mentioned above
let phones = [...new Set(allPersons.map(person => person.phone))];
then I tried to return all the unique phones, but unsuccessfully:
let result = phones.map(phone => phone.id).filter((value, index, self) => self.indexOf(value) === index)
This returns only the unique ids of the phones, but I want the entire object. What can I do?
UPDATE:
phone Ids are NOT unique, e.g. nokia3310 has id 1, nokia3330 has id 2, etc: so tom and john can have the same phone and phone ids could be duplicated!
Make an object indexed by IDs instead, then take the object's values:
const phonesById = Object.fromEntries(
allPersons.map(
({ phone }) => [phone.id, phone]
)
);
const uniquePhones = Object.values(phonesById);
If you're trying to get the phone object in each object of the array, then the code below will do that for you.
It gets the phone object and stores it in a
var objArr = [
{id: "abcdefg", name: "tom", phone: {brand: "blah", id: "hijklm"}},
{id: "guidiuqwbd", name: "john", phone: {brand: "hihihih", id: "ayfva"}},
{id: "yuygeve", name: "doe", phone: {brand: "hahahah", id: "cqcqw"}}
]
var allPhoneObjects = [];
objArr.forEach(function(currObj){
var phoneObj = currObj.phone;
allPhoneObjects.push(phoneObj);
});
console.log(allPhoneObjects);
I propose you the following solution
let uniques = new Set();
const phones = allPersons.filter(({phone}) => (
uniques.has(phone.id) ? false : !!uniques.add(phone.id)
)).map(p => p.phone)
Basically, we define a Set to record ids of the phones already processed, and a filter function on the allPersons array, that returns only the phones not already in the Set. We complete with the map to extract only the portion of JSON needed
EDIT
You can use just one function on the allPersons array using the reduce function
let uniques = new Set();
const phones = allPersons.reduce( (filtered, {phone}) => {
if (!uniques.has(phone.id)) {
filtered.push(phone);
uniques.add(phone.id);
}
return filtered
}, [])
I would like to combine elements of 2 arrays based on the name. For example:
Array1 = [
{name: "name1", language: "lang1"},
{name: "name2", language: "lang2"},
{name: "name3", language: "lang3"}]
Array2 = [
{name: "name1", subject: "sub1"},
{name: "name2", subject: "sub2"},
{name: "name3", subject: "sub3"}]
I need to generate the following array:
Array3 = [
{language: "lang1", subject: "sub1"},
{language: "lang2", subject: "sub2"},
{language: "lang3", subject: "sub3"}]
The logic I could think of was to write an explicit for loop to compare every element of first array with every element of second array and check if name matches as shown below.
let Array3 = []
for(let i=0;i<Array1.length;i++)
{
let elem = Array1[i];
for(let j=0;j<Array2.length;j++)
{
if(Array2[j].name===elem.name)
{
Array3.append({language: elem.language, subject: Array2[j].subject})
break;
}
}
}
However, my actual dataset is quite large and this seems inefficient. How can this can be achieved in a more efficient manner (like using higher order functions or something)?
Using a Map for O(1) lookup of one of the arrays using name as key lets you iterate each array only once.
const Array1=[{name:"name1",language:"lang1"},{name:"name2",language:"lang2"},{name:"name3",language:"lang3"}],Array2=[{name:"name1",subject:"sub1"},{name:"name2",subject:"sub2"},{name:"name3",subject:"sub3"}];
const a1Map = new Map(Array1.map(({name, ...r})=> [name, {...r}]));
const res = Array2.map(({name, ...r}) => ({...r, ...a1Map.get(name)}))
console.log(res)
You need to iterate over the two arrays and group the generated object in a map having the name as the key:
let Array1 = [
{name: "name1", language: "lang1"},
{name: "name2", language: "lang2"},
{name: "name3", language: "lang3"}
];
let Array2 = [
{name: "name1", subject: "sub1"},
{name: "name2", subject: "sub2"},
{name: "name3", subject: "sub3"}
];
let map = new Map();
Array1.forEach(e => map.set(e.name, {language: e.language}));
Array2.forEach(e => {
if(map.has(e.name))
map.set(e.name, {...map.get(e.name), subject: e.subject});
});
let Array3 = [...map.values()].filter(e => e.language && e.subject);
console.log(Array3);
Yes you are thinking in right order , you need to use the sort algorithm logics , I will say nested for loops will be just as good. With larger dataset , since you need to extract the values from two different array you can use the nested for loops.
for(int i=0;i>array1.length();i++){
This can be use for first array
Define String x=",";
For second
for(int j=0;j>array2.length();j++)
{
Check if ( (","+j+",").contains(x)) then break;
If array1 name found in array 2, store array3 as you want
Also Store value of j in x
Like x=x +j+",";
}}
This way your nested for loop will skip the comparison code.
Above algo is raw but will reduce the complexity a significant bit.
I'm not sure if I phrased the question right. I'm fairly new to JavaScript, and I'd like to add multiple objects (?) to an array.
If I have this array:
let arr = [{
firstname: "John",
lastname: "Smith"
}];
How would I add, say
var firstname = "John";
var lastname = "Doe";
as
{ firstname: "John", lastname: "Doe" }
to the same array?
Items can be added to an array with the push method. Every array has this method build it, together with many other methods, and can be used to push a new value to the end of the array.
var arr = [
{
firstname: "John",
lastname: "Smith"
}
];
In the push method create an object with the keys and the values that you want to add.
var firstname = "John";
var lastname = "Doe";
arr.push({
firsName: firstName,
lastName: lastName
});
If the keys of the object are the same name as the variables then you can use the syntax below. This will give the object keys with the same name as the variable and set the value of the variable with it as the value of the key.
arr.push({ firstName, lastName });
Alternatively if you want to add an object to the beginning of the array, use the unshift method of the array.
arr.unshift({ firstName, lastName });
You can also do this with spread operator:
var arr = [{firstname: "John", lastname: "Smith"}];
arr= [...arr, {firstname: "Name1", lastname: "LName"}]
console.log(arr);
Take a look at push.
let arr = [{
firstname: "John",
lastname: "Smith"
}];
var firstname = "soham";
var lastname = "s";
arr.push({
firstname: firstname,
lastname: lastname
})
console.log(arr);
I have the following object:
personObj = {
_id : '123',
first_name: 'John',
last_name: 'Doe',
}
I would like to destructure it to the following variables:
id, <-- _id
name: {
first, <-- first_name
last <-- last_name
}
(I want first_name and last_name to reside inside a 'name' object)
I've tried the following syntax:
const {
id: _id,
name: {
first: first_name,
last: last_name
}
} = personObj
However this causes an error.
What am I doing wrong?
Update
Chapter 10. Destructuring of book "Exploring ES 6" provides many advanced examples of how to use destructuring and explains how it works internally.
Destructuring can extract values directly into the properties of an object. The properties are not required to exist but all destination objects must already exist when the destructuring assignment happens.
Armed with this knowledge, the code that answers the question is:
let personObj = {
_id: '123',
first_name: 'John',
last_name: 'Doe',
}
// Create the objects that receive the values on destructuring
let c = { name: {} }
// Do the magic
({ _id: c.id, first_name: c.name.first, last_name: c.name.last } = personObj)
console.log(c)
// {id: "123", name: {first: "John", last: "Doe"}}
The parentheses around the assignment expression that uses destructuring are required, without them the engine reports a syntax error at the first :.
The original answer follows. It doesn't completely answer the question but I leave it here for reference. It shows how to use the rest properties (...) in destructuring expressions and it was accepted by the OP, as incomplete as it is.
The original answer
Destructuring with properties renaming works the other way around: the original name is placed before the colon, the new name is after it.
let personObj = {
_id: '123',
first_name: 'John',
last_name: 'Doe',
}
// Destructure personObj using different names for the properties
const {
_id: id,
first_name: first,
last_name: last
} = personObj
console.log('id: ' + id);
console.log('first: ' + first);
console.log('last: ' + last);
// Output
// id: 123
// first: John
// last: Doe
You can then assemble the pieces (id, first, last) into a new object:
let c = {
id,
name: {
first,
last
}
}
console.log(c);
// Output
// { id: '123', name: { first: 'John', last: 'Doe' } }
Update
The most similar result to what you describe in the question can be achieved by:
let { _id: id, ...name } = personObj
console.log(id)
console.log(name)
// Output
// 123
// { first_name: 'John', last_name: 'Doe' }
But this way the properties of name use the same names they have in personObj. Even more, it doesn't work any more if you add to personObj properties after last_name that you don't want to copy in name.