Let's say I start with this:
var shippingAddresses = [
{
"firstname": "Kevin",
"lastname": "Borders",
"address1": "2201 N Pershing Dr",
"address2": "Apt 417",
"city": "Arlington",
"state": "VA",
"zip": "22201",
"country": "US"
},
{
"firstname": "Dan",
"lastname": "Hess",
"address1": "304 Riversedge Dr",
"address2": "",
"city": "Saline",
"state": "MI",
"zip": "48176",
"country": "US"
}
]
I use this to prepopulate a form.
Users can edit entries or add new ones. I need to prevent them from adding duplicates.
The issue is that the structure of the form that I am serializing and the order these values are returned from the database are not the same, so there is a chance that I will insert an item into this array with the following format:
{
"country": "US",
"firstname": "Kevin",
"lastname": "Borders",
"address1": "2201 N Pershing Dr",
"address2": "Apt 417",
"zip": "22201",
"city": "Arlington",
"state": "VA"
}
Which is the same as the first entry, just ordered differently.
I am loading underscorejs, so if there's a way to handle it with that library that would be great. I'm also using jQuery if that helps.
At this point I'm not sure how to proceed.
The Underscore findWhere function does exactly what you need - it's not an indexOf search by object identity, but searches objects whose properties have the same values as the input.
if (_.findWhere(shippingAddresses, toBeInserted) == null) {
shippingAddresses.push(toBeInserted);
}
Basic example using lodash union method:
var a = [1,2,3];
// try to add "1" and "4" to the above Array
a = _.union(a, [1, 4]);
console.log(a);
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.min.js"></script>
While this doesn't directly answers the question, it does answers the broader question of how to add unique values to an Array, and like myself, others might stumble upon this page from google.
based on this answer to: "js-remove-an-array-element-by-index-in-javascript"
https://stackoverflow.com/a/7142909/886092
I'm using the following idiom that is concise and does not require underscore.js or any other framework.
Here is an example for recording selected and deselected rows for DataTables jquery plugin.
I keep an array of currently selected ids, and I don't want to end up with duplicates in the array:
in coffeescript
fnRowSelected: (nodes) ->
position = $selected.indexOf(nodes[0].id)
unless ~position
$selected.push nodes[0].id
return
fnRowDeselected: (nodes) ->
position = $selected.indexOf(nodes[0].id)
if ~position
$selected.splice(position, 1)
More generally it would
position = myArray.indexOf(myval)
unless ~position
myArray.push myVal
or in JS
var position;
position = myArray.indexOf(myval);
if (!~position) {
myArray.push(myVal);
}
If you want to check the user input object you could try this function:
var uniqueInput = {
"country": "UK",
"firstname": "Calvin",
"lastname": "Borders",
"address1": "2201 N Pershing Dr",
"address2": "Apt 417",
"city": "Arlington",
"state": "VA",
"zip": "22201"
};
var duplicatedInput = {
"country": "US",
"firstname": "Kevin",
"lastname": "Borders",
"address1": "2201 N Pershing Dr",
"address2": "Apt 417",
"city": "Arlington",
"state": "VA",
"zip": "22201"
};
var shippingAddresses = [{
"firstname": "Kevin",
"lastname": "Borders",
"address1": "2201 N Pershing Dr",
"address2": "Apt 417",
"city": "Arlington",
"state": "VA",
"zip": "22201",
"country": "US"
}, {
"firstname": "Dan",
"lastname": "Hess",
"address1": "304 Riversedge Dr",
"address2": "",
"city": "Saline",
"state": "MI",
"zip": "48176",
"country": "US"
}];
function checkDuplication(checkTarget,source){
_.each(source,function(obj){
if(_.isEqual(checkTarget,obj)){
alert("duplicated");
}
});
}
And try to invoke this check function in different parameter (uniqueInput and duplicatedInput)
I think it could check the duplication input in your shipping addresses.
checkDuplication(uniqueInput,shippingAddresses);
checkDuplication(duplicatedInput,shippingAddresses);
I make a jsfiddle. You could try it.
Hope this is helpful for you.
EDIT, this will work with your example of unsorted properties:
var normalized_array = _.map(shippingAddresses, function(a){
var o = {};
_.each(Object.keys(shippingAddresses[0]), function(x){o[x] = a[x]});
return o;
})
var stringy_array = _.map(normalized_array, JSON.stringify);
shippingAddresses = _.map(_.uniq(stringy_array), JSON.parse});
and we could do this with a one-liner but it would be super ugly:
shippingAddresses_uniq = _.map(_.uniq(_.map(_.map(shippingAddresses, function(a){ var o = {}; _.each(Object.keys(shippingAddresses[0]), function(x){o[x] = a[x]}); return o; }), JSON.stringify)), JSON.parse});
I think you need this,
NOTE: No library is required.
let array = [{ id: 1}, {id: 2}, {id: 3}];
function addUniqeObj(data) {
let index = -1;
for(let i = 0, i < array.length; i++) {
if(array[i].id === data.id) {
index = i;
}
}
if(index > -1) {
array[index] = data;
} else {
array.push(data)
}
}
Basic example using Set() from ECMAScript 2015 (no library required)
The Set object lets you store unique values of any type (whether primitive values or object references). If an iterable object is passed, all of its elements will be added to the new Set. Here I'll just add one value:
// original array with duplicates already present
const numbers = [1, 1, 1, 2, 3, 100]
// Use Set to remove duplicate elements from the array
// and keep your new addition from causing a duplicate.
// New value (100) is not added since it exists (and array
// also is de-duped)
console.log(Array.from(new Set([...numbers, 100])))
// [1, 2, 3, 100]
// New, non-existing value (101) is added (and array is de-duped)
console.log(Array.from(new Set([...numbers, 101])))
// [1, 2, 3, 100, 101]
Related
How can I get the 'address' array within of request response object below?
{
"title": "MRS.",
"name": "Aluno",
"lastName": "3",
"birthday": "2019-08-31",
"address": [
{
"addressName": "rua 2",
"zipCode": "13901264",
"city": "amparo",
"state": "sp",
"country": "brasil"
},
{
"addressName": "rua 2",
"zipCode": "13901264",
"city": "amparo",
"state": "sp",
"country": "brasil"
},
]
}
If I save this object in a state called customer and print console.log(customer.address) it works well and I can see the address informations, but I can't use map or forEach methods like customer.address.forEach, I receive an error :(
You can use methods such as map, reduce, filter, forEach only on Array Not on Object.
the key address has value as an object, To read it you can simply use
console.log(customer.address.addressName) //street x
console.log(customer.address.zipCode) //13901264
If you want to loop through properties
Object.values(customer.address).forEach((value) => {
console.log(value);
})
// OR
Object.keys(customer.address).forEach((key) => {
console.log(customer.address[key]);
})
The property address it not an array, if you need to convert it into an array of one element you can use:
const valueAsArray = [response.address]
Object.keys(out.address).map(key => {
console.log(out.address[key]);
// etc ...
})
I m trying to get all the numbers from the list of all the contacts. Im probably not using forEach correctly any advice? I've put a sample of what is expected
//sample of a contact
Object {
"company": "Financial Services Inc.",
"contactType": "person",
"firstName": "Hank",
"id": "2E73EE73-C03F-4D5F-B1E8-44E85A70F170",
"imageAvailable": false,
"jobTitle": "Portfolio Manager",
"lastName": "Zakroff",
"middleName": "M.",
"name": "Hank M. Zakroff",
"phoneNumbers": Array [
Object {
"countryCode": "us",
"digits": "5557664823",
"id": "337A78CC-C90A-46AF-8D4B-6CC43251AD1A",
"label": "work",
"number": "(555) 766-4823",
},
Object {
"countryCode": "us",
"digits": "7075551854",
"id": "E998F7A3-CC3C-4CF1-BC21-A53682BC7C7A",
"label": "other",
"number": "(707) 555-1854",
},
],
},
//Expected
numbers = [
5557664823,
7075551854
]
//does not work
const numbers = contacts.map(contact => contact.phoneNumbers.forEach(number));
forEach always returns undefined, so your map callback returns undefined, so numbers will be full of undefineds.
I think you probably want to return the phone numbers (each number in the phoneNumbers array of each entry), and then perhaps flatten the result:
const numbers = contacts.map(contact => contact.phoneNumbers.map(({number}) => number)).flat();
Array.prototype.flat is relatively new, but easily polyfilled.
That's such a common pattern that there's a flatMap method to do it in one go:
const numbers = contacts.flatMap(contact => contact.phoneNumbers.map(({number}) => number));
Or just a simple loop with push:
const numbers = [];
for (const {phoneNumbers} of contacts) {
numbesr.push(...phoneNumbers.map(({number}) => number));
}
Probably want to use reduce and map
let numbers = contacts.reduce((p, c, i) => {
return p.concat(c.phoneNumbers.map(pn => pn.number));
}, []);
I don't know how many times I've done that. forEach doesn't return anything.
const numbers = contacts.reduce((n, c)=>(a.concat(contact.phoneNumbers)),[]);
or
const numbers = contacts.reduce((n, c)=>(a.concat(contact.phoneNumbers.map(pn=>pn.number)),[]);
I have an array of people that has objects with details about them. For example, "Bob Cratchit" might have an age of 30, a set of skills: "JS, CSS, Python", and a salary of "113000". I want to first check if he has a salary of over 100000, and if so, return his first and last name.
I have tried various methods of .map and .filter, and even tried nesting them. My .map works when I use the console, returning all the names, and the .filter returns all info on people that have > 100000 salary, but I can't seem to do both.
let people = [{
"id": 1,
"firstName": "Frank",
"lastName": "Herbert",
"job": "Lead Software Engineer",
"Skills": ["JavaScript", "C#", "SQL", "HTML", "CSS", "SCRUM
Master"],
"Salary": 120196
},
{
"id": 2,
"firstName": "Joan",
"lastName": "Armorett",
"job": "Jr Software Developer",
"Skills": ["JavaScript", "HTML", "CSS"],
"Salary": 70000
}
// This is the .map function, which will show me all of the names,
regardless of their salary.
let answer = people.map((person) => {return person.firstName});
// This is the .filter function, which will show me all data, not
just names on everyone with a salary of 100000 or higher.
let answer = people.filter((person) => {
return person.Salary > 100000;
});
What I would like is some way to have both: only show results of people who make 100000 or more, and only show those people's names, not other data on them.
The code below uses filter and map functions chained together. Filter returns an array of all the objects having salary property>100000 and map takes the array returned from filter and returns the objects firstname and lastname property . In the end the array is spread out with the spread operator (...) returning it as text.
let people = [{
"id": 1,
"firstName": "Frank",
"lastName": "Herbert",
"job": "Lead Software Engineer",
"Skills": ["JavaScript", "C#", "SQL", "HTML", "CSS", "SCRUMMaster"],
"Salary": 120196
},
{
"id": 2,
"firstName": "Joan",
"lastName": "Armorett",
"job": "Jr Software Developer",
"Skills": ["JavaScript", "HTML", "CSS"],
"Salary": 70000
}]
var t=people.filter((e)=>e.Salary>100000).map((e)=>e.firstName+" "+e.lastName)
console.log(...t)
You could use reduce to do this:
const peopleWhoMakeOverHundredThou = people.reduce((acc, el) => {
if (el.Salary > 100000){
const name = `${el.firstName} ${el.lastName}`
acc.push(name);
}
return acc;
},[])
console.log(peopleWhoMakeOverHundredThou)
If you're only expecting one to be higher and wanted to return a single string instead of any array, you can just adjust as follows:
const peopleWhoMakeOverHundredThou = people.reduce((acc, el) => {
if (el.Salary > 100000){
const name = `${el.firstName} ${el.lastName}`
acc+=name;
}
return acc;
},'')
console.log(peopleWhoMakeOverHundredThou)
I am attempting to use Apps Script to convert the values in a sheet to a multi-level JSON.
It is easy enough to convert the values to a single level JSON like this:
[{
"name": "Bob Jones",
"phone": "555-555-5555",
"street": "123 Somewhere St.",
"city": "Nowhere",
"state": "ID",
"postal": 45632,
"country": "USA"
}]
But, what I want, is this:
[{
"name": "Bob Jones",
"phone": "555-555-5555",
"address": {
"street": "123 Somewhere St.",
"city": "Nowhere",
"state": "ID",
"postal": 45632,
"country": "USA"
}
}]
Here is the code used to format the JSON:
function makeJSON_(object, options) {
if (options.format == FORMAT_PRETTY) {
var jsonString = JSON.stringify(object, null, 4);
} else if (options.format == FORMAT_MULTILINE) {
var jsonString = Utilities.jsonStringify(object);
jsonString = jsonString.replace(/},/gi, '},\n');
jsonString = prettyJSON.replace(/":\[{"/gi, '":\n[{"');
jsonString = prettyJSON.replace(/}\],/gi, '}],\n');
} else {
var jsonString = Utilities.jsonStringify(object);
}
return jsonString;
}
It would be easy enough to setup a "pre-conversion" sheet to create the JSON substring, but that isn't flexible and would be a beast to maintain.
How do I JSON.stringify() the sheet data to automatically create the substrings?
To go from the version of json you have to the one you want to go to you can do the following --
var json = [{
"name": "Bob Jones",
"phone": "555-555-5555",
"street": "123 Somewhere St.",
"city": "Nowhere",
"state": "ID",
"postal": 45632,
"country": "USA"
}]
for (var i=0; i < json.length; i++){
var currentObj = json[i];
// make a temporary address object
var address = {};
// copy all the attributes over to the temp object
address.street = currentObj.street;
address.city = currentObj.city;
address.state = currentObj.state;
address.postal = currentObj.postal;
address.country = currentObj.country;
// add address to the original object
currentObj.address = address;
// get rid of the following attributes from parent
delete currentObj.street;
delete currentObj.city;
delete currentObj.state;
delete currentObj.postal;
delete currentObj.country;
}
console.log(json);
Much easier than replacing things in a string.
http://codepen.io/anon/pen/XKOrXa
I am attempting to create a Google map with data from two separate json files. I'm trying to use jquery/javascript to combine the two files and then process the resulting array of objects through the Google Maps API.
I've tried $.extend, $.merge, concat, and I've even tried pushing the data into an empty array, but in each case only the first set of data is appearing on the map (although I can see both sets of data separately if I display them with console.log).
I must be doing something fundamentally wrong, but I'm not seeing it. The relevant part of my code is as follows (with the things I've tried commented out). Any suggestions would be most appreciated.
j$.when(
j$.getJSON('mapData1.js'),
j$.getJSON('mapData2.js')
).done(function(data1, data2) {
var d1 = data1;
var d2 = data2;
var d3 = [];
d3.push(d1[0]);
d3.push(d2[0]);
//var d3 = j$.extend({},d1,d2);
//var d3 = j$.merge(d1,d2);
//var d3 = d1.concat(d2);
var data = d3[0];
//code to process data with Google Maps API
});
My json files look like this (but with many more items):
[
{
"ID": "a001a000002o4iZAAQ",
"NAME": "Atlanta",
"Address": "123 State Street",
"City": "Atlanta",
"StateAbbreviation": "GA",
"SF": "",
"LeaseExpiration": "8/31/2012",
"Occupancy": "2",
"Country": "USA",
"Address2": "",
"Lat": "33.7863317",
"Lng": "-84.3836873",
"Type": "loc",
"Color": "red"
}
]
you can use concat()
var array1 = [{
"ID-1": "a001a000002o4iZAAQ",
"NAME-1": "Atlanta",
"Address-1": "123 State Street",
"City-1": "Atlanta",
"StateAbbreviation-1": "GA",
"SF-1": "",
"LeaseExpiration-1": "8/31/2012",
"Occupancy-1": "2",
"Country-1": "USA",
"Address2-1": "",
"Lat-1": "33.7863317",
"Lng-1": "-84.3836873",
"Type-1": "loc",
"Color-1": "red"
}];
var array2 = [{
"ID-2": "a001a000002o4iZAAQ",
"NAME-2": "Atlanta",
"Address-2": "123 State Street",
"City-2": "Atlanta",
"StateAbbreviation-2": "GA",
"SF-2": "",
"LeaseExpiration-2": "8/31/2012",
"Occupancy-2": "2",
"Country-2": "USA",
"Address2-2": "",
"Lat-2": "33.7863317",
"Lng-2": "-84.3836873",
"Type-2": "loc",
"Color-2": "red"
}];
var array3 = array1.concat(array2);
alert(JSON.stringify(array3));