How to array map instead of nested for loops - javascript

For my scenario, I need to push elements to an addresses array which contains objects. I'm working with vue.js.
My current working function is:
propagateCustomerInfo(selectedOption, id){
// Propagate addresses
this.addresses = selectedOption.addresses
// Propagate contact's addresses
for (var i = selectedOption.contacts.length - 1; i >= 0; i--) {
for (var j = selectedOption.contacts[i].addresses.length - 1; j >= 0; j--) {
let address = selectedOption.contacts[i].addresses[j]
address.contact = selectedOption.contacts[i]
this.addresses.push(address)
}
}
},
the selectedOption object has the below structure:
{
addresses: [
{
id: 0,
street: 'My street'
},
{...}
],
contacts: [
{
id: 0,
name: 'Lorem Ipsum',
addresses: [
{
id: 0,
street: 'My street'
},
{...}
],
}
]
}
Besides pushing every contact's address object to this.addresses array I need to append the contact to the address itself for multiselect rendering purposes. That's why I'm doing address.contact = selectedOption.contacts[i]
I almost sure that this can be accomplished in a prettiest way with some mapping/reduce combination but I can't figure out how to do it.
Any help will be really appreciated.
Thanks!

if you want to combine all address in contact variable to addresses variable:
this.contacts.map(contact => this.addresses.push(...contact.addresses))
Edit.
to inject the contact.id and contact.name:
this.contacts.map(contact => {
let temp = []
contact.addresses.map(address => {
temp.push({
name: contact.name,
id: contact.id,
...address
})
})
this.addresses.push(...temp)
})

Related

Loop Array of Objects

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));
}
});

Remove duplicates and merge values of Javascript array

I have a javascript array like so:
var recipients = [{
name: 'Michael',
task: 'programming',
contactdetails: 'michael#michael.com'
}, {
name: 'Michael',
task: 'designing',
contactdetails: 'michael#michael.com'
}, {
name: 'Shane',
task: 'designing',
contactdetails: 'shane#shane.com'
}];
What I am doing is a rostering system where I send out notifications for who is on for this week, so the email is like "Hi Michael you are programming this week". At the moment it is not great because it sends out an email for every value in the array. So in the above instance it would send Michael 2 emails.
What I would like to do is remove duplicates while merging the task property strings. So the array would be:
var recipients = [{
name: 'Michael',
task: 'programming, designing',
contactdetails: 'michael#michael.com'
}, {
name: 'Shane',
task: 'designing',
contactdetails: 'shane#shane.com'
}];
that way it can just send one message like "Hi Michael you are programming, designing this week". How do I go about this? I also am using Google Apps script so I need a pure javascript solution. I should also add that the name and email address for each person will always be identical, so Michael will never have a different email address etc. Your help is much appreciated!
This would be a good opportunity to use the reduce function.
What we do is cycle through each of the original recipients list, see if we have already processed the element, if we have, append the task of the current element to the already processed element, otherwise, add the current recipient to the processed list
// original array
var recipients = [
{name: 'Michael',task:'programming',contactdetails:'michael#michael.com'},
{name: 'Michael',task:'designing',contactdetails:'michael#michael.com'},
{name: 'Shane',task:'designing',contactdetails:'shane#shane.com'}
];
var recipientKeyList = []; // used to store the contacts we've already processed
// cycle through each recipient element
var newRecipients = recipients.reduce(function(allRecipients, recipient){
// get the indexOf our processed array for the current recipient
var index = recipientKeyList.indexOf(recipient.contactdetails);
// if the contact details already exist, append the task
if( index >= 0){
allRecipients[index].task = allRecipients[index].task + ', ' + recipient.task;
return allRecipients
}else{ // otherwise append the recipient
recipientKeyList.push(recipient.contactdetails)
return allRecipients.concat(recipient);
}
}, []);
var recipients = [{name: 'Michael',task:'programming',contactdetails:'michael#michael.com'},{name: 'Michael',task:'designing',contactdetails:'michael#michael.com'},{name: 'Shane',task:'designing',contactdetails:'shane#shane.com'}];
var tempObj = {};
for (i=0; i<recipients.length; i++) {
if (!tempObj[recipients[i]['name']]) {
tempObj[recipients[i]['name']] = {};
tempObj[recipients[i]['name']]['task'] = [];
}
tempObj[recipients[i]['name']]['task'].push(recipients[i]['task']);
tempObj[recipients[i]['name']]['contactdetails'] = recipients[i]['contactdetails'];
}
var new_arr = [];
Object.keys(tempObj).forEach(function(key) {
new_arr.push({name: key, task: tempObj[key]['task'].join(", "), contactdetails: tempObj[key]['contactdetails']})
});
Iterate and look for same object if then append tasks like this
var recipients = [{
name: 'Michael',
task: 'programming',
contactdetails: 'michael#michael.com'
}, {
name: 'Michael',
task: 'designing',
contactdetails: 'michael#michael.com'
}, {
name: 'Shane',
task: 'designing',
contactdetails: 'shane#shane.com'
}];
var uniqueR = [];
var copyRecipients = JSON.parse(JSON.stringify(recipients));
copyRecipients .forEach(function(ele){
var obj = uniqueR.find(function(e){
return (e.name == ele.name && e.contactdetails == ele.contactdetails);
});
if(obj){
obj.task = obj.task + ", " + ele.task;
}else{
uniqueR.push(ele);
}
});
console.log(uniqueR)
Convert array into an object with key as name (can be email also)
// original array
var recipients = [
{name: 'Michael',task:'programming',contactdetails:'michael#michael.com'},
{name: 'Michael',task:'designing',contactdetails:'michael#michael.com'},
{name: 'Shane',task:'designing',contactdetails:'shane#shane.com'}
];
var recipientsObj = {};
for (var i = 0; i < recipients.length; i++) {
var element = recipients[i];
var recipientInObj = recipientsObj[element.name]
if (recipientInObj) {
// If a recipient is repeated with same task, here duplicates will appear
recipientInObj.task += ', ' + element.task;
} else {
recipientsObj[element.name] = element;
}
}
console.log(recipientsObj)
var newJSON = {};
$.each(recipients, function(i, json){
newJSON[json.contactdetails] = {name : json["name"], task : newJSON[json.contactdetails]!= undefined && newJSON[json.contactdetails]["task"]!= undefined ? newJSON[json.contactdetails]["task"] + ", " + json["task"] : json["task"] }
});

Fill in missing properties in an array of objects

What is the best way to fill in missing properties in an array of objects, such as this example:
[
{
name: 'Tom',
number: '01234 567 890',
website: 'http://www.tom.com'
},
{
name: 'Richard',
number '07777 666 555'
},
{
name: 'Harry',
website: 'http://www.harry.com'
}
]
I need to add the missing properties with a null value, so that when I pass this array on to be rendered in something such as a HTML table or CSV file, everything lines up correctly. I was thinking of passing over the array twice, once to get all the possible properties, and a second time to add those missing properties with a null value to each object where it doesn't exist. Is there a better way to do this?
EDIT: I won't know what the keys are until I have the data, it's coming from an API and the keys are not always requested explicitly.
My final solution
Thanks all, it seems the two pass approach is indeed the best approach. After I started to write this using the examples provided, I realised that the order of the properties wasn't being maintained. This is how I achieved filling in the missing props, and maintaining the correct order. Any suggestions for potential improvements are welcome.
var fillMissingProps = function(arr) {
// build a list of keys in the correct order
var keys = [];
arr.forEach(function(obj) {
var lastIndex = -1;
Object.keys(obj).forEach(function(key, i) {
if (keys.includes(key)) {
// record the position of the existing key
lastIndex = keys.lastIndexOf(key);
if (lastIndex < i) {
// this key is in the wrong position so move it
keys.splice(i, 0, keys.splice(lastIndex, 1)[0]);
lastIndex = i;
}
} else {
// add the new key in the correct position
// after the previous existing key
lastIndex++;
keys.splice(lastIndex, 0, key);
}
});
});
// build a template object with all props set to null
// and in the correct position
var defaults = {};
keys.forEach(function(key) {
defaults[key] = null;
});
// and update the array by overwriting each element with a
// new object that's built from the template and the original object
arr.forEach(function(obj, i, arr) {
arr[i] = Object.assign({}, defaults, obj);
});
return arr;
};
/** TEST **/
var currentArray = [
{
website: 'http://www.unknown.com'
},
{
name: 'Tom',
number: '01234 567 890',
website: 'http://www.tom.com'
},
{
title: 'Mr',
name: 'Richard',
gender: 'Male',
number: '04321 666 555'
},
{
id: '003ABCDEFGHIJKL',
name: 'Harry',
website: 'http://www.harry.com',
mobile: '07890 123 456',
city: 'Brentwood',
county: 'Essex'
}
];
var newArray = fillMissingProps(currentArray);
for (var i = 0; i < newArray.length; i++) {
for (var prop in newArray[i]) {
console.log(prop + ": " + newArray[i][prop]);
}
console.log('---------');
}
Given that you don't know apriori which keys are supposed to exist, you have no choice but to iterate over the array twice:
// build a map of unique keys (with null values)
var keys = {}
array.forEach(el => Object.keys(el).forEach(k => keys[k] = null));
// and update the array by overwriting each element with a
// new object that's built from the null map and the original object
array.forEach((el, ix, a) => a[ix] = Object.assign({}, keys, el));
Use Array.prototype.map():
const arr = [
{
name: 'Tom',
number: '01234 567 890',
website: 'http://www.tom.com',
},
{
name: 'Richard',
number: '07777 666 555',
},
{
name: 'Harry',
website: 'http://www.harry.com',
},
];
const newArr = arr.map(x => (
arr.map(x => Object.keys(x))
.reduce((a, b) =>
(b.forEach(z => a.includes(z) || a.push(z)), a)
)
.forEach(
y => (x[y] = x.hasOwnProperty(y) ? x[y] : null)
), x)
);
console.log(newArr);
Here is a more interesting answer, its a tad fun one but it will build up your objects on the fly as new properties appear:
var currentArray = [
{
name: 'Tom',
number: '01234 567 890',
website: 'http://www.tom.com'
},
{
name: 'Richard',
number: '07777 666 555'
},
{
name: 'Harry',
website: 'http://www.harry.com'
}
]
var newArray = []
function NewObject() {
}
for(var i = 0; i < currentArray.length; i++){
var nObj = new NewObject();
for(var prop in currentArray[i]){
if(!NewObject.hasOwnProperty(prop))
NewObject.prototype[prop] = null;
nObj[prop]=currentArray[i][prop];
}
newArray.push(nObj);
}
for(var i = 0; i < newArray.length; i++){
for(var prop in newArray[i]){
console.log(prop+ ": "+newArray[i][prop]);
}
console.log('---------');
}
It builds new objects from the ones you provide and adds new properties to the objects if they don't exist already.
This idea was more for curiosities sake tho so any comments would be interesting :)
You can get all keys and set all keys using for..of loop, .map() to iterate all Object.keys(), redefine original array
var arr = [{
name: 'Harry',
website: 'http://www.harry.com'
},{
name: 'Tom',
number: '01234 567 890',
website: 'http://www.tom.com'
}, {
name: 'Richard',
number: '07777 666 555'
}];
for (var obj of arr) {
for (var key of Object.keys(obj)) {
arr = arr.map(o => (o[key] = o[key] || null, o))
}
};
console.log(arr);
Something like this could work:
for (var i = 0; i < arrayLength; i++) {
yourArray[i].name = yourArray[i].name || null;
yourArray[i].number = yourArray[i].number || null;
yourArray[i].website= yourArray[i].website|| null;
}

Add array as element of another Array

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];
});

"Store" a lot of data

I'm new to javascript and I'm not sure about the best way to store data.
I'll write an example about what I want to obtain:
Country1
Country informations
Region1
Region informations
Place1
Place informations
Place2
Place informations
Region2
Region informations
Place1
Place informations
Place2
Place informations
Country2
Country informations
Region1
Region informations
Place1
Place informations
Place2
Place informations
Region2
Region informations
Place1
Place informations
Place2
Place informations
...
Consider "informations" as different values like x, y, name, ecc...
I want to be able to access these properties in an easy way, something like 1-1-2-x
The best solution that came to my mind was to use a 4d array to store "place informations", a 3d one to store "Region informations" and a matrix to store "country informations"
Do you think this is the best solution? I hope my explanation is understandable, thanks.
I think JSON is what you're after.
Example:
var data = {
countries : [
{
name: 'country1',
regions: [
{
name: 'region1',
places: [
{
name: 'place1'
}
]
}
]
}
]
};
Here we have an array of countries. Each country can have an array of regions. Each region, an array of places.
You can consume this by indexes:
var place = data.contries[0].regions[0].places[0].name;
Or you can write custom methods to access by name:
var data = {
getCountry : function(name) {
var len = this.countries.length;
for (var i = 0; i < len; i++) {
if (name === this.countries[i].name)
return this.countries[i];
}
return null;
},
countries : [
{
getRegion : function(name) {
var len = this.regions.length;
for (var i = 0; i < len; i++) {
if (name === this.regions[i].name)
return this.regions[i];
}
return null;
},
name: 'country1',
regions: [
{
name: 'region1',
places: [
{
name: 'place1'
}
]
}
]
}
]
};
Usage:
var region = data.getCountry('county1').getRegion('region1');
Verbose data example:
var data = {
countries : [
{
name: 'country1',
regions: [
{
name: 'region1',
places: [
{
name: 'place1'
},
{
name: 'place2'
}
]
},
{
name: 'region2',
places: [
{
name: 'place3'
},
{
name: 'place4'
}
]
}
]
},
{
name: 'country2',
regions: [
{
name: 'region3',
places: [
{
name: 'place5'
},
{
name: 'place6'
}
]
},
{
name: 'region4',
places: [
{
name: 'place7'
},
{
name: 'place8'
}
]
}
]
}
]
};
You could create JSON objects for your data and store in a MongoDB. There's some documentation here how to store and retrieve the data.
There's an article here for further understanding, so you can see how to create the JSON object(s), it will then be up to you how you want to store them.
Storing this amount of data in client-side without Ajax requests is not suggested. However what will I do at this moment is this:
var data= new Array()
data["US"] = new Array();
data["US"]["Info"] = "United Stats Information...";
data["US"]["Arizona"] = new Array();
data["US"]["Arizona"]["Info"] = "Region Info...";
data["US"]["Arizona"]["placeA"] = "PlaceA Info!";
data["US"]["Arizona"]["placeB"] = "PlaceB Info!";

Categories