I have an array like this
$scope.dogs = [
{ id: 1, breed: 'German Shepherd' },
{ id: 2, breed: 'Collie' }
]
And a second array like this:
$scope.owners = [
{ name: 'Mary', breedowned: 'German Shepherd' },
{ name: 'Bill', breedowned: 'German Shepherd' },
{ name: 'Bob', breedowned: 'Collie' }
]
I want to push the list of owners into the list of dogs like so basically creating:
$scope.dogs = [
{ id: 1, breed: 'German Shepherd', owners: [...] }
]
I tried to use forEach and push the owners into the dogs array, but it does not work.
angular.forEach($scope.dogs, function (value, key) {
for (x = 0; x < $scope.owners.length; x++) {
if ($scope.owners[i].breedowned == value.breed) {
$scope.dogs[key].owners.push($scope.owners[i])
}
}
});
Thanks for any help!
If you don't want any form of dependency, just use Array.prototype.push.apply, this way:
Array.prototype.push.apply($scope.owners, $scope.dogs);
You didnt mention any errors, but I see an issue with you missing var in front of the x in the for loop, and also owners is not initialized in the dog object. Here's a consistent nested loop solution:
angular.forEach($scope.dogs, function (dog) {
angular.forEach($scope.owners, function (owner) {
if (owner.breedowned == dog.breed) {
dog.owners = dog.owners || []
dog.owners.push(owner)
}
})
})
Here a better solution that only goes through the owners array once and only through the dogs array once.
var tracker = $scope.owners.reduce(function(trackerObj, owner){
var breedowned = owner.breedowned;
trackerObj[breedowned] = trackerObj[breedowned] || [];
trackerObj[breedowned].push(owner);
return trackerObj;
}, {});
$scope.dogs.forEach(function(dog){
dog.owners = tracker[dog.breed];
});
Related
In relation to my previous question: Add key and incremental values to array of objects
#ran.t 's solution worked perfectly. But now I am trying to write a function to reuse the solution, I am getting undefined.
No function:
let arr_obj = [
{
name: 'Hermione',
order: 'books',
},
{
name: 'Harry',
order: 'brooms',
},
{
name: 'Ron',
order: 'food',
}
];
let order_size = 100;
arr_obj.forEach(d => {
d['order_size'] = order_size;
order_size -= 25;
});
console.log(arr_obj)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Calling using the function (need for resuability):
let arr_obj = [
{
name: 'Hermione',
order: 'books',
},
{
name: 'Harry',
order: 'brooms',
},
{
name: 'Ron',
order: 'food',
}
];
let order_size = 100;
const myFnc = (arr) => {
arr.forEach(d => {
d['order_size'] = order_size;
order_size -= 25;
});
}
console.log(myFnc(arr_obj));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Why am I getting undefined when all I am doing is putting the forEach inside the function?
You forgot to return arr, bro.
First time posting on here and was hoping to get some help I can't seem to figure out how to do this problem. It's basically to create a function that receives an array of objects that returns a new object.
For some reason, push won't go through and returns the error property of push is undefined.
const organizeInstructors = function(instructors) {
let output = {}; // so the obvious which is to create the object
for (let i = 0; i < instructors.length; i++) {
if (!output[instructors[course]]) {
output[instructors[course]] = instructors[course];
} else {
output[instructors[course]].push(instructors[name]);
}
}
return output;
};
console.log(organizeInstructors([{
name: "Samuel",
course: "iOS"
},
{
name: "Victoria",
course: "Web"
},
{
name: "Karim",
course: "Web"
},
{
name: "Donald",
course: "Web"
}
]));
expected output
{
iOS: ["Samuel"],
Web: ["Victoria", "Karim", "Donald"]
}
Thanks for any advice or hints you guys can give
Looks like you were getting a bit confused iterating over arrays vs keying into objects.
let organizeInstructors = function(instructors) {
let output = {}; // so the obvious which is to create the object
for(let i = 0; i < instructors.length; i++) {
const instructor = instructors[i]
if(!output[instructor.course]) {
output[instructor.course] = []
}
output[instructor.course].push(instructor.name)
}
return output;
}
console.log(organizeInstructors([
{name: "Samuel", course: "iOS"},
{name: "Victoria", course: "Web"},
{name: "Karim", course: "Web"},
{name: "Donald", course: "Web"}
]))
Adding the const instructor makes it much easier to read as well
This uses the Array.prototype.reduce method.
Please note that this will not check if the value is already present in the course array and will just blindly add in. This could mean that you get multiple instances of the same name in the same course.
const organizeInstructors = function(instructors) {
return instructors.reduce((cumulative, current) => {
// if we don't have a course in cumulative object, add it.
if (!cumulative[current.course]) cumulative[current.course] = [];
// add in the current name.
cumulative[current.course].push(current.name);
// return the cumulative object for the next iteration.
return cumulative;
}, {});
}
console.log(organizeInstructors([{
name: "Samuel",
course: "iOS"
},
{
name: "Victoria",
course: "Web"
},
{
name: "Karim",
course: "Web"
},
{
name: "Donald",
course: "Web"
}
]));
Using reduce
data = [ { name: "Samuel", course: "iOS" }, { name: "Victoria", course: "Web" }, { name: "Karim", course: "Web" }, { name: "Donald", course: "Web" }, ];
getObj = (data) =>
data.reduce(
(r, c) => (
!r[c.course] // checks if accumulator doesn't have c.course as key
? ((r[c.course] = []), r[c.course].push(c.name)) // then make an array that corresponds the key then push c.name
: r[c.course].push(c.name), // else push c.name to the corresponding array
r
),
{}
);
console.log(getObj(data));
I want to add new property to an array from another array using indexes of both.
For example, lets say I have the following array:
initArray = [
{ name: 'John', family: 'Doe'},
{ name: 'Joanna', family: 'Doe'}
];
And the array that I want to take from it and push it into initArray:
genderArray = [
{ gender: 'Male' },
{ gender: 'Female' }
];
What I need is a final array that be like:
initArray = [
{ name: 'John', family: 'Doe', gender: 'Male'},
{ name: 'Joanna', family: 'Doe', gender: 'Female'}
];
I tried the following:
ngOnInit() {
Object.keys(this.genderArray).forEach((idx) => {
console.log(this.initArray)
this.initArray[idx].push(this.genderArray)
});
console.log(this.initArray)
}
But it returned an error of:
Error: _this.initArray[idx].push is not a function
Here is a stackblitz.
First, you should not push to initArray: you do not add elements to initArray, but instead add a property to each of its elements.
One possible way to do that:
Object.assign(this.initArray[idx], this.genderArray[idx]);
... or, if for some reason you want to create a completely new array of new 'enriched' objects, instead of modifying objects directly:
this.initArray[idx] = {...this.initArray[idx], ...this.genderArray[idx]};
Second, do not use Object.keys() to go through an array: use either forEach() if you do all the transformations inside the function or map, if you return a new element and want to have a new array in the end.
So your function might look like that:
ngOnInit() {
this.genderArray.forEach((element, index) => {
Object.assign(this.initArray[index], element);
});
console.log(this.initArray)
}
... or ...
ngOnInit() {
this.initArray = this.initArray.map((element, index) => {
{ ...element, ...this.genderArray[index] }
});
console.log(this.initArray)
}
Personally, I always find code 'x = x.map(transformer())` highly suspicious (if one reassign anyway, why not use forEach?), but it's up to implementation style choices.
If there's more than property in the elements of an array to be 'mixed', but you only want to take one, you can either go direct:
Object.assign(this.initArray[idx], { gender: this.genderArray[idx].gender });
... use destructuring in assignment:
const { gender } = this.genderArray[idx];
Object.assign(this.initArray[idx], { gender });
... or just bite the bullet and do assignment straight as we did in the good old syntax-sugar-free days:
this.initArray[idx].gender = this.genderArray[idx].gender;
In this particular case the last one looks like the best approach actually. Its disadvantage is having to repeat the property name, but that might also come handy if you want the props to be named differently in source and target arrays.
You're actually trying to "merge" objects in two arrays.
There are many ways to implement this, but I think the clearest one is with the Array.map and Object.assign functions:
const initArray = [
{ name: 'John', family: 'Doe'},
{ name: 'Joanna', family: 'Doe'}
];
const genderArray = [
{ gender: 'Male' },
{ gender: 'Female' }
];
const ans = initArray.map((person, index) => {
const genderedPerson = Object.assign({}, person, genderArray[index]);
return genderedPerson;
});
console.log(ans);
Array.map iterates an array and modifies its elements.
Object.assign extends an object, adding it additional properties from another object(s).
You can merge two arrays by reduce method using ... spread operator:
const result = initArray.reduce((a, c, i) => {
a.push({...c, ...genderArray[i]})
return a;
},[])
or one line version:
const result = initArray.reduce((a, c, i) => (a.push({...c, ...genderArray[i]}), a),[])
An example:
let initArray = [
{ name: 'John', family: 'Doe'},
{ name: 'Joanna', family: 'Doe'}
];
let genderArray = [
{ gender: 'Male' },
{ gender: 'Female' }
];
const result = initArray.reduce((a, c, i) => {
a.push({...c, ...genderArray[i]})
return a;
},[])
console.log(result)
The problem is that you're trying to use array.push() on an object, which doesn't work.
Instead, you could use a for loop to go through the array, and simply add a 'gender' category to that index by assigning a value to it, namely the gender at that index in the other array:
initArray = [
{ name: 'John', family: 'Doe'},
{ name: 'Joanna', family: 'Doe'}
];
genderArray = [
{ gender: 'Male' },
{ gender: 'Female' }
];
for (var i = 0; i < initArray.length; i++) {
initArray[i].gender = genderArray[i].gender;
}
console.log(initArray);
Alternatively, you could use array.forEach():
initArray = [
{ name: 'John', family: 'Doe'},
{ name: 'Joanna', family: 'Doe'}
];
genderArray = [
{ gender: 'Male' },
{ gender: 'Female' }
];
initArray.forEach(addGender);
function addGender(_, i) {
initArray[i].gender = genderArray[i].gender;
}
console.log(initArray);
I have to return Greg and Joe from this array of objects.
I am looping through the array and if the master is Emily I want to alert() Greg and Joe, review.name.
let list = [
{
master: "Leo",
review: [{
name: 'Bob',
stars: 2
},
{
name: 'Elly',
stars: 4
},
]
},
{
master: "Emily",
review: [{
name: 'Greg',
stars: 3
},
{
name: 'Joe',
stars: 2
},
]
},
]
for (let i = 0; i < list.length; i++) {
if (list[i].master === 'Emily') {
alert(list[i].review.name)
}
}
It doesn't alert anything.
That is because review is an array of objects & you need to pass index. In this demo passing 0 to get the first object. You again loop review and get the name from each object
let list = [
{
master: "Leo",
review: [{
name: 'Bob',
stars: 2
},
{
name: 'Elly',
stars: 4
},
]
},
{
master: "Emily",
review: [{
name: 'Greg',
stars: 3
},
{
name: 'Joe',
stars: 2
},
]
},
]
for (let i = 0; i < list.length; i++) {
if (list[i].master === 'Emily') {
alert(list[i].review[0].name)
}
}
review is an array of objects. We could one-line this like so, but I'll explain what we're doing:
alert(
list.find(e => e.master === "Emily").review
.map(e => e.name).join(', ')
)
list is an array, which has a find prototype. In the find method, we pass a function that returns true or false. We can decide what we're trying to find. In this case, we're trying to find the entry where the master property is "Emily". Having found what we're looking for, we can use map to loop through the entry's review property, and return an array of names which we finally stick together using join.
Another approach would be to use 2 loops:
for (let i = 0; i < list.length; i++) {
if (list[i].master === 'Emily') {
let names = [];
for (let j = 0; j < list[i].review.length; j++) {
names.push(list[i].review[j].name);
}
alert(names.join(', '));
}
}
Keep in mind that the first approach will throw an error if no entry with master property set to "Emily" can be found.
You can do:
const list = [{master: "Leo",review: [{name: 'Bob',stars: 2},{name: 'Elly',stars: 4},]},{master: "Emily",review: [{name: 'Greg',stars: 3},{name: 'Joe',stars: 2},]}]
list.forEach((o, i) => {
if (o.master === 'Emily') {
list[i].review.forEach(u => console.log(u.name));
}
});
Do you have an optimised solution for the following.
let say,
x = { users: [ {id: 1}, {id:2}, {id:3} ] }
I want to make a new key with same values, the output should be,
{ users: { list: [ {id: 1}, {id:2}, {id:3} ], count: 3 }
Using only JS or Underscore with one line code w/o any extra effort.
I know other tricks to do the same, but do we have one line solution for the same ?
Help appreciated...
Thanks.
Create the object, and assign the x array to list. However, count should be a getter, since the list length might change:
const x = { users: [{ id: 1 }, { id: 2 }, { id: 3 }] };
const result = {
users: {
list: x.users,
get count() { return this.list.length; }
}
};
console.log(JSON.stringify(result));
result.users.list.push({ id: 4 });
console.log(JSON.stringify(result));
I'm not sure why this is to be an optimised solution because this is a simple plain-JavaScript problem:
let x = { users: [{ id: 1 }, { id: 2 }, { id: 3 }] };
let result = {
users: {
list: x.users,
count: x.users.length
}
};
console.log(result);
Sure, just define the property as an object
const obj = {
users: [{
id: 1
}, {
id: 2
}, {
id: 3
}]
};
obj.users = { list: obj.users, count: obj.users.length };
console.log(obj);
I recommend focusing on code clarity rather than on line conservation though