how to merge objects in javascript without override values - javascript

how can i merge duplicate key in objects and concat values in objects in one object
i have objects like this
var object1 = {
role: "os_and_type",
value: "windows"
};
var object2 = {
role: "os_and_type",
value: "Android"
};
var object3 = {
role: "features",
value: "GSM"
};
how can i achieve this object
new_object = [{
role: "os_and_type",
value: ["windows", "android"]
}, {
role: "features",
value: ["GSM"]
}];

Here you go:
var object1 = {
role: "os_and_type",
value: "windows"
};
var object2 = {
role: "os_and_type",
value: "Android"
};
var object3 = {
role: "features",
value: "GSM"
};
function convert_objects(){
var output = [];
var temp = [];
for(var i = 0; i < arguments.length; i++){ // Loop through all passed arguments (Objects, in this case)
var obj = arguments[i]; // Save the current object to a temporary variable.
if(obj.role && obj.value){ // If the object has a role and a value property
if(temp.indexOf(obj.role) === -1){ // If the current object's role hasn't been seen before
temp.push(obj.role); // Save the index for the current role
output.push({ // push a new object to the output,
'role':obj.role,
'value':[obj.value] // but change the value from a string to a array.
});
}else{ // If the current role has been seen before
output[temp.indexOf(obj.role)].value.push(obj.value); // Save add the value to the array at the proper index
}
}
}
return output;
}
Call it like this:
convert_objects(object1, object2, object3);
You can add as many objects to the function as you'd like.

Too bad that we haven't seen any attempt.
function merge(array) {
var temp = {},
groups = [],
l = array.length,
i = 0,
item;
while (item = array[i++]) {
if (!temp[item.role]) {
temp[item.role] = {
role: item.role,
value: [item.value]
};
} else if (temp[item.role].value.indexOf(item.value) === -1) {
temp[item.role].value.push(item.value);
}
}
for (var k in temp) {
groups.push(temp[k]);
}
return groups;
}
Usage :
var groups = merge([object1, object2, object3]);

Here's a version using maps to avoid scanning for duplicates over and over. Also using some cool methods
Object.keys
Array.prototype.map
Array.prototype.forEach
It ended up being slightly smaller too.
function merge(objects) {
var roles = {};
objects.forEach(function(obj){
roles[obj.role] = roles[obj.role] || {};
roles[obj.role][obj.value] = {};
});
return Object.keys(roles).map(function(role){
return {
role: role,
value: Object.keys(roles[role])
};
});
}
http://jsfiddle.net/mendesjuan/cD7uu/1/

Related

Using for loop to add elements to an empty array [duplicate]

I created an array of objects like so:
[
{
"lat": 12.123,
"lng": 13.213,
"city": "New York"
},
{
"lat": 3.123,
"lng": 2.213,
"city": "New York"
},
{
"lat": 1.513,
"lng": 1.113,
"city": "London"
}
]
I'm trying to create a new array that filters the places to only contains objects that don't have the same city property (lat/lng duplicates are ok). Is there a built in JS or Jquery function to achieve this?
I'd probably use a flags object during the filtering (edit: I wouldn't anymore, see the note at the end of the answer about ES2015's Set), like this:
var flags = {};
var newPlaces = places.filter(function(entry) {
if (flags[entry.city]) {
return false;
}
flags[entry.city] = true;
return true;
});
That uses Array#filter from ECMAScript5 (ES5), which is one of the ES5 additions that can be shimmed (search for "es5 shim" for several options).
You can do it without filter, of course, it's just a bit more verbose:
var flags = {};
var newPlaces = [];
var index;
for (index = 0; index < places.length; ++index) {
if (!flags[entry.city]) {
flags[entry.city] = true;
newPlaces.push(entry);
}
});
Both of the above assume the first object with a given city should be kept, and all other discarded.
Note: As user2736012 points out below, my test if (flags[entry.city]) will be true for cities with names that happen to be the same as properties that exist on Object.prototype such as toString. Very unlikely in this case, but there are four ways to avoid the possibility:
(My usual preferred solution) Create the object without a prototype: var flags = Object.create(null);. This is a feature of ES5. Note that this cannot be shimmed for obsolete browsers like IE8 (the single-argument version of Object.create can be except when that argument's value is null).
Use hasOwnProperty for the test, e.g. if (flags.hasOwnProperty(entry.city))
Put a prefix on that you know doesn't exist for any Object.prototype property, such as xx:
var key = "xx" + entry.city;
if (flags[key]) {
// ...
}
flags[key] = true;
As of ES2015, you could use a Set instead:
const flags = new Set();
const newPlaces = places.filter(entry => {
if (flags.has(entry.city)) {
return false;
}
flags.add(entry.city);
return true;
});
Shortest, but not best performance (see update bellow) solution for es6 :
function unique(array, propertyName) {
return array.filter((e, i) => array.findIndex(a => a[propertyName] === e[propertyName]) === i);
}
performance: https://jsperf.com/compare-unique-array-by-property
You can filter using a Set by only including elements with a property value that has not yet been added to the Set (after which it should be added to the Set). This can be accomplished in one line using the logical and operator (&&). Using this data structure has the advantage of sublinear lookup times (often O(1)).
Below is a general function to obtain a unique array of objects based on a specific property (prop) from an array of objects (arr). Note that in the case of duplicates, only the first object with the property value will be retained.
const getUniqueBy = (arr, prop) => {
const set = new Set;
return arr.filter(o => !set.has(o[prop]) && set.add(o[prop]));
};
Demo:
var places = [{
lat: 12.123,
lng: 13.213,
city: 'New York'
}, {
lat: 3.123,
lng: 2.213,
city: 'New York'
}, {
lat: 3.123,
lng: 4.123,
city: 'Some City'
}];
const getUniqueBy = (arr, prop) => {
const set = new Set;
return arr.filter(o => !set.has(o[prop]) && set.add(o[prop]));
};
console.log(getUniqueBy(places, 'city'));
https://lodash.com/docs#uniqBy
https://github.com/lodash/lodash/blob/4.13.1/lodash.js#L7711
/**
* This method is like `_.uniq` except that it accepts `iteratee` which is
* invoked for each element in `array` to generate the criterion by which
* uniqueness is computed. The iteratee is invoked with one argument: (value).
*
* #static
* #memberOf _
* #since 4.0.0
* #category Array
* #param {Array} array The array to inspect.
* #param {Array|Function|Object|string} [iteratee=_.identity]
* The iteratee invoked per element.
* #returns {Array} Returns the new duplicate free array.
* #example
*
* _.uniqBy([2.1, 1.2, 2.3], Math.floor);
* // => [2.1, 1.2]
*
* // The `_.property` iteratee shorthand.
* _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
* // => [{ 'x': 1 }, { 'x': 2 }]
*/
I expanded a bit on #IgorL solution, but extended prototype and gave it a selector function instead of a property to make it a little more flexible:
Array.prototype.unique = function(selector) {
return this.filter((e, i) => this.findIndex((a) => {
if (selector) {
return selector(a) === selector(e);
}
return a === e;
}) === i);
};
Usage:
// with no param it uses strict equals (===) against the object
let primArr = ['one','one','two','three','one']
primArr.unique() // ['one','two','three']
let a = {foo:123}
let b = {foo:123}
let fooArr = [a,a,b]
fooArr.unique() //[a,b]
// alternatively, you can pass a selector function
fooArr.unique(item=>item.foo) //[{foo:123}] (first "unique" item returned)
Definitely NOT the most performant way to do this but as long as the selector is simple and the array isn't massive, it should work fine.
In Typescript
Array.prototype.unique = function<T>(this: T[], selector?: (item: T) => object): T[] {
return this.filter((e, i) => this.findIndex((a) => {
if (selector) {
return selector(a) === selector(e);
}
return a === e;
}) === i);
};
My suggestion :
Array.prototype.uniqueCity = function() {
var processed = [];
for (var i=this.length-1; i>=0; i--){
if (processed.indexOf(this[i].city)<0) {
processed.push(this[i].city);
} else {
this.splice(i, 1);
}
}
}
in use :
places.uniqueCity();
or
Array.prototype.uniqueObjectArray = function(field) {
var processed = [];
for (var i=this.length-1; i>=0; i--) {
if (this[i].hasOwnProperty(field)) {
if (processed.indexOf(this[i][field])<0) {
processed.push(this[i][field]);
} else {
this.splice(i, 1);
}
}
}
}
places.uniqueObjectArray('city');
With the above you can sort the array by any of the fields in the objects, even if they are not present for some of the objects.
or
function uniqueCity(array) {
var processed = [];
for (var i=array.length-1; i>=0; i--){
if (processed.indexOf(array[i].city)<0) {
processed.push(array[i].city);
} else {
array.splice(i, 1);
}
}
return array;
}
places = uniqueCity(places);
You could use a Map so the entries with the same key property (in your case 'city') only appear once
module.exports = (array, prop) => {
const keyValueArray = array.map(entry => [entry[prop], entry]);
const map = new Map(keyValueArray);
return Array.from(map.values());
};
More info about Map and array objects here
Basic example on Codepen
Another option:
const uniqueBy = prop => list => {
const uniques = {}
return list.reduce(
(result, item) => {
if (uniques[item[prop]]) return result
uniques[item[prop]] = item
return [...result, item]
},
[],
)
}
const uniqueById = uniqueBy('id')
uniqueById([
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
{ id: 1, name: 'one' },
{ id: 3, name: 'three' }
])
You can paste it on your console to see it working.
It should work for the scenario presented and a few others.
We can create the list of unique objects by any property using JavaScript Map.
For example :
var places = [{ 'lat': 12.123, 'lng': 13.213, 'city': "New York"},
{ 'lat': 3.123, 'lng': 2.213, 'city': "New York"},
{ 'lat': 43.123, 'lng': 12.213, 'city': "London"}];
var cityMap = new Map();
places.forEach(p=> cityMap.set(p.city, p));
console.log([...cityMap.values()]);
Execute code snippet to see the result.
As pointed out in the comments, you could use an object as a map, which will allow you to avoid duplicates, you can then enumerate the properties of the object.
working fiddle: http://jsfiddle.net/gPRPQ/1/
var places = [];
var a = {};
a.lat = 12.123;
a.lng = 13.213;
a.city = "New York";
places.push(a);
var b = {};
b.lat = 3.123;
b.lng = 2.213;
b.city = "New York";
places.push(b);
var unique = {}
for (var i = 0; i < places.length; i++) {
var place = places[i];
unique[place.city] = place;
}
for (var name in unique) {
var place = unique[name];
console.log(place);
}
var places = [];
var a = {};
a.lat = 12.123;
a.lng = 13.213;
a.city = "New York";
places.push(a);
var b = {};
b.lat = 3.123;
b.lng = 2.213;
b.city = "New York";
places.push(b);
getUniqAR(places,'city'); //Return Uniq Array by property
function getUniqAR(Data,filter){
var uniar =[];
Data.forEach(function(item,ind,arr){
var dupi=false;
if(!uniar.length) uniar.push(item) //push first obj into uniq array
uniar.forEach(function(item2, ind2,arr){
if(item2[filter] == item[filter]){ //check each obj prop of uniq array
dupi=true; //if values are same put duplicate is true
}
})
if(!dupi){ uniar.push(item)} //if no duplicate insert to uniq
})
console.log(uniar)
return uniar;
}
In simple Javascript code to remove duplicate cities from places array list is
var places = [{ 'lat': 12.123, 'lng': 13.213, 'city': "New York"},
{ 'lat': 3.123, 'lng': 2.213, 'city': "New York"},
{ 'lat': 43.123, 'lng': 12.213, 'city': "London"}];
var unique = [];
var tempArr = [];
places.forEach((value, index) => {
if (unique.indexOf(value.city) === -1) {
unique.push(value.city);
} else {
tempArr.push(index);
}
});
tempArr.reverse();
tempArr.forEach(ele => {
places.splice(ele, 1);
});
console.log(places);
Generic Typescript answer based on https://stackoverflow.com/a/18773857/49564 above:
export function isDistinct<T>(mapper: (value: T) => string): (value: T) => boolean {
const keys: { [index: string]: boolean } = {};
return (entry: T) => {
const key = mapper(entry);
if (keys[key] !== undefined) {
return false;
}
return keys[key] = true;
};
}
// Usage example:
const items = [ { id: 1 }, { id: 2 }, { id: 3 }, { id: 1 } ];
const unique = items.filter(isDistinct(i => i.id));
I think you want 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)
}
}
Another variation of the rafaelbiten approach:
const dedupExample = [
{id: 1, c: 'whatever'},
{id: 1, c: '1whatever'},
{id: 2, c: '2whatever'},
{id: 2, c: '2whatever'},
{id: 3, c: '2whatever'},
]
const getUniqueBy = (prop, list) => {
const objUniq = list.reduce((res, item) => ({ ...res, [item[prop]]: item }), {})
return Object.keys(objUniq).map(item => objUniq[item])
}
const uniq = getUniqueBy('id', dedupExample)
console.info('info', { uniq })
/* [
{id: 1, c: 'whatever'},
{id: 2, c: '2whatever'},
{id: 3, c: '2whatever'},
] */
const distinctArrayByCity= [
...new Map(array.map((item) => [item.city, item])).values(),
];
This thread may be old but thought I should share it. It is based on Pure JavaScript and removes Duplicate Objects based on the Properties Specified.
function removeDuplicates(originalArray, properties) {
var newArray = [];
var index = 0;
var lookupObject = {};
var totalProperties = properties.length;
for (var i = 0; i < originalArray.length; i++) {
var exists = false;
for (var a = 0; a < newArray.length; a++) {
var propsFound = 0;
for (var b = 0; b < totalProperties; b++) {
if (originalArray[i][properties[b]] == newArray[a][properties[b]]) {
propsFound++;
}
}
//If there is a match then break the for loop
if (propsFound == totalProperties) {
exists = true;
break;
}
} //End of New Array
if (!exists) {
newArray[index] = originalArray[i];
index++;
}
} //End of originalArray
return newArray;
}
You can view the fiddle here

Find a property (which is stored as a string in a variable) in an array of objects

I have an array of objects which store the data of different 'pupils'.
var pupils = [
{
id: 0,
name: 'will'
},
{
id: 1,
name: 'megan'
}
];
I want to create a function called 'findPupil' which takes three parameters: the property you know, the value you know it to be and the property you want to find.
Say you know that the pupil you are looking for has an id of 1 but you don't know their name. In this case, you would call the function like this:
var name = findPupil('id', 1, 'name'); // should now store 'Megan' as a string
Here is the function I have written:
function findPupil(property, value, find) {
pupils.forEach(function(pupil) {
if(pupils[pupil][`${property}`] === value) {
return pupils[pupil][`${find}`];
}
});
}
Calling this function returns the following:
Error: Cannot read property 'id' of undefined
How do I make this function work?
Use Array.find() to find an object with the property you know, or a default if an object is not found. Extract the find property from the object:
const pupils = [{"id":0,"name":"will"},{"id":1,"name":"megan"}];
const findPupil = (property, value, find) =>
(pupils.find(o => o[property] === value) || {})[find];
const name = findPupil('id', 1, 'name');
console.log(name);
You could use .find() for that.
var pupils = [{id: 0, name: 'will'},{id: 1,name: 'megan'}];
function getByPropertyAndValue(knownProp, knownValue, desiredProperty) {
let pupil = pupils.find(p => p[knownProp] === knownValue); //Find pupil by property and value
return pupil && pupil[desiredProperty]; //Return the value, or undefined if not found
}
console.log(getByPropertyAndValue("id", 1, "name"));
Most of your code is correct except for the part of your if statement. Please find working example -
var pupils = [{
id: 0,
name: 'will'
},
{
id: 1,
name: 'megan'
}
];
function findPupil(property, value, find) {
let result
pupils.forEach(function(pupil) {
if (pupil[property] === value) {
result = pupil[find];
}
});
return result
}
var name = findPupil('id', 1, 'name');
console.log(name)
var pupils = [
{ id: 0, name: 'will' },
{ id: 1, name: 'megan' }
];
function findPupil(property, value, find) {
var find_value = '';
for (var i = 0; i < pupils.length; i++) {
if (find_value == '') {
Object.keys(pupils[i]).forEach(function (key) {
if (key == property && pupils[i][key] == value) {
find_value = pupils[i][find];
}
});
} else {
break;
}
}
console.log(find_value);
return find_value;
}
var name = findPupil('id', 1, 'name');

JavaScript - iterate through object and change nested properties

This is an object to be processed:
var q = {
email: {contains: "noname#hotmail.com"},
name: {contains: "someuser"}
};
I would like to go through each key of q and if the corresponding value is an object that has the property contains then replace it with $regex.
Related information can be found here: JavaScript: Object Rename Key
You can try the following way:
var q = {
email: {contains: "noname#hotmail.com"},
name: {contains: "someuser"}
};
for(var k in q){
if(q[k].hasOwnProperty('contains')){
Object.defineProperty(q[k], '$regex',
Object.getOwnPropertyDescriptor(q[k], 'contains'));
delete q[k]['contains'];
}
}
console.log(q);
for(const obj of Object.values(q)) {
obj.$regex = obj.contains;
delete obj.contains;
}
Just go over all values inside q and copy the contains property into the $regex property.
To iterate over object keys first you have to fetch them, here is one simple approach
const keys = Object.keys(q); // ["email", "name"]
Now iterate over the array which we got and perform regex testing;
keys.forEach(key => {
let value = q[key].contains;
// create $regex and assign value
// remove .contains
})
You can loop through the objects and first put current value of contains property in $regex and then delete the contains property.
Below is working code:
var q = {
email: {
contains: "noname#hotmail.com"
},
name: {
contains: "someuser"
}
};
for (var i of Object.values(q)) {
if (i.hasOwnProperty("contains")) {
i.$regex = i.contains;
delete i.contains;
}
}
console.log(q);
var q = {
email: {contains: "noname#hotmail.com"},
name: {contains: "someuser"}
};
Object.keys(q).forEach(k => {
if (typeof q[k].contains != 'undefined'){
q[k].$regex = q[k].contains;
delete q[k].contains;
}
})
console.log(q);
Other version using Es 6 features
const renameProp = (
oldProp,
newProp,
{ [oldProp]: old, ...others }
) => {
return {
[newProp]: old,
...others
};
};
let q = {
email: {contains: "noname#hotmail.com"},
name: {contains: "someuser"}
};
let newObj = {}
for (let propName in q) {
newObj[propName] = renameProp("contains","$regex",q[propName])
}
console.log(newObj)
var q = {
email: {
contains: "noname#hotmail.com"
},
name: {
contains: "someuser"
},
asdf: "asdf"
};
Object.keys(q).forEach(function(item, index) {
if (typeof q[item] == "object" && q[item].contains) {
q[item].$regex = q[item].contains;
delete q[item].contains;
}
})

Alternative for _.invert() using underscore library

I need to change the existing map swapping keys into values and values into keys. As there is duplicate values in my map for the keys I cannot use _.invert() of underscore library.
function map() {
return {
'eatables': {
apple: 'fruits',
orange: 'fruits',
guava: 'fruits',
brinjal: 'vegetables',
beans: 'vegetables',
rose: 'flowers',
}
}
}
var reverseMap = _.invert(map()['eatables']);
// invert function works for distinct values.
console.log (reverseMap);
// which is giving Object {fruits: "guava", vegetables: "brinjal",flowers:"rose"}
But i am expecting an output as
Object {fruits: ["apple","orange","guava"], vegetables: ["brinjal","beans"], flowers:"rose"}
I tried as below, i just stuck how to find whether map value is distinct or multiple?
var newObj = invert(map()['eatables']);
_.each(newObj, function(key) {
if (Array.isArray(key)) {
_.each( key, function(value) {
console.log(value);
});
} else {
console.log("else:"+key);
}
});
function invert(srcObj) {
var newObj = {};
_.groupBy(srcObj, function(value, key ) {
if (!newObj[value]) newObj[value] = []; //Here every thing is array, can i make it string for values which are unique.
newObj[value].push(key);
});
return newObj;
}
Let me any alternative using underscore library.
You can use this function. This function uses Object.keys to generate an array containing the keys of the object passed in input. Then, it accesses the values of the original object and use them as key in the new object. When two values map to the same key, it pushes them into an array.
function invert(obj) {
var result = {};
var keys = Object.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
if (result[obj[keys[i]]] instanceof Array) {
result[obj[keys[i]]].push(keys[i])
} else if (result[obj[keys[i]]]) {
var temp = result[obj[keys[i]]];
result[obj[keys[i]]] = [temp, keys[i]];
} else {
result[obj[keys[i]]]=keys[i];
}
}
return result;
}
https://jsfiddle.net/6f2ptxgg/1/
You can use the underscore each to iterate through your data and push the result in an array. It should give you your expected output.
function customInvert(data) {
var result = {};
_.each(data, function (value, key) {
if (_.isUndefined(result[value])) {
result[value] = key;
} else if(_.isString(result[value])) {
result[value] = [result[value], key];
} else {
result[value].push(key)
}
});
return result;
}
customInvert({
apple: 'fruits',
orange: 'fruits',
guava: 'fruits',
brinjal: 'vegetables',
beans: 'vegetables',
rose: 'flowers',
})

Create array of unique objects by property

I created an array of objects like so:
[
{
"lat": 12.123,
"lng": 13.213,
"city": "New York"
},
{
"lat": 3.123,
"lng": 2.213,
"city": "New York"
},
{
"lat": 1.513,
"lng": 1.113,
"city": "London"
}
]
I'm trying to create a new array that filters the places to only contains objects that don't have the same city property (lat/lng duplicates are ok). Is there a built in JS or Jquery function to achieve this?
I'd probably use a flags object during the filtering (edit: I wouldn't anymore, see the note at the end of the answer about ES2015's Set), like this:
var flags = {};
var newPlaces = places.filter(function(entry) {
if (flags[entry.city]) {
return false;
}
flags[entry.city] = true;
return true;
});
That uses Array#filter from ECMAScript5 (ES5), which is one of the ES5 additions that can be shimmed (search for "es5 shim" for several options).
You can do it without filter, of course, it's just a bit more verbose:
var flags = {};
var newPlaces = [];
var index;
for (index = 0; index < places.length; ++index) {
if (!flags[entry.city]) {
flags[entry.city] = true;
newPlaces.push(entry);
}
});
Both of the above assume the first object with a given city should be kept, and all other discarded.
Note: As user2736012 points out below, my test if (flags[entry.city]) will be true for cities with names that happen to be the same as properties that exist on Object.prototype such as toString. Very unlikely in this case, but there are four ways to avoid the possibility:
(My usual preferred solution) Create the object without a prototype: var flags = Object.create(null);. This is a feature of ES5. Note that this cannot be shimmed for obsolete browsers like IE8 (the single-argument version of Object.create can be except when that argument's value is null).
Use hasOwnProperty for the test, e.g. if (flags.hasOwnProperty(entry.city))
Put a prefix on that you know doesn't exist for any Object.prototype property, such as xx:
var key = "xx" + entry.city;
if (flags[key]) {
// ...
}
flags[key] = true;
As of ES2015, you could use a Set instead:
const flags = new Set();
const newPlaces = places.filter(entry => {
if (flags.has(entry.city)) {
return false;
}
flags.add(entry.city);
return true;
});
Shortest, but not best performance (see update bellow) solution for es6 :
function unique(array, propertyName) {
return array.filter((e, i) => array.findIndex(a => a[propertyName] === e[propertyName]) === i);
}
performance: https://jsperf.com/compare-unique-array-by-property
You can filter using a Set by only including elements with a property value that has not yet been added to the Set (after which it should be added to the Set). This can be accomplished in one line using the logical and operator (&&). Using this data structure has the advantage of sublinear lookup times (often O(1)).
Below is a general function to obtain a unique array of objects based on a specific property (prop) from an array of objects (arr). Note that in the case of duplicates, only the first object with the property value will be retained.
const getUniqueBy = (arr, prop) => {
const set = new Set;
return arr.filter(o => !set.has(o[prop]) && set.add(o[prop]));
};
Demo:
var places = [{
lat: 12.123,
lng: 13.213,
city: 'New York'
}, {
lat: 3.123,
lng: 2.213,
city: 'New York'
}, {
lat: 3.123,
lng: 4.123,
city: 'Some City'
}];
const getUniqueBy = (arr, prop) => {
const set = new Set;
return arr.filter(o => !set.has(o[prop]) && set.add(o[prop]));
};
console.log(getUniqueBy(places, 'city'));
https://lodash.com/docs#uniqBy
https://github.com/lodash/lodash/blob/4.13.1/lodash.js#L7711
/**
* This method is like `_.uniq` except that it accepts `iteratee` which is
* invoked for each element in `array` to generate the criterion by which
* uniqueness is computed. The iteratee is invoked with one argument: (value).
*
* #static
* #memberOf _
* #since 4.0.0
* #category Array
* #param {Array} array The array to inspect.
* #param {Array|Function|Object|string} [iteratee=_.identity]
* The iteratee invoked per element.
* #returns {Array} Returns the new duplicate free array.
* #example
*
* _.uniqBy([2.1, 1.2, 2.3], Math.floor);
* // => [2.1, 1.2]
*
* // The `_.property` iteratee shorthand.
* _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
* // => [{ 'x': 1 }, { 'x': 2 }]
*/
I expanded a bit on #IgorL solution, but extended prototype and gave it a selector function instead of a property to make it a little more flexible:
Array.prototype.unique = function(selector) {
return this.filter((e, i) => this.findIndex((a) => {
if (selector) {
return selector(a) === selector(e);
}
return a === e;
}) === i);
};
Usage:
// with no param it uses strict equals (===) against the object
let primArr = ['one','one','two','three','one']
primArr.unique() // ['one','two','three']
let a = {foo:123}
let b = {foo:123}
let fooArr = [a,a,b]
fooArr.unique() //[a,b]
// alternatively, you can pass a selector function
fooArr.unique(item=>item.foo) //[{foo:123}] (first "unique" item returned)
Definitely NOT the most performant way to do this but as long as the selector is simple and the array isn't massive, it should work fine.
In Typescript
Array.prototype.unique = function<T>(this: T[], selector?: (item: T) => object): T[] {
return this.filter((e, i) => this.findIndex((a) => {
if (selector) {
return selector(a) === selector(e);
}
return a === e;
}) === i);
};
My suggestion :
Array.prototype.uniqueCity = function() {
var processed = [];
for (var i=this.length-1; i>=0; i--){
if (processed.indexOf(this[i].city)<0) {
processed.push(this[i].city);
} else {
this.splice(i, 1);
}
}
}
in use :
places.uniqueCity();
or
Array.prototype.uniqueObjectArray = function(field) {
var processed = [];
for (var i=this.length-1; i>=0; i--) {
if (this[i].hasOwnProperty(field)) {
if (processed.indexOf(this[i][field])<0) {
processed.push(this[i][field]);
} else {
this.splice(i, 1);
}
}
}
}
places.uniqueObjectArray('city');
With the above you can sort the array by any of the fields in the objects, even if they are not present for some of the objects.
or
function uniqueCity(array) {
var processed = [];
for (var i=array.length-1; i>=0; i--){
if (processed.indexOf(array[i].city)<0) {
processed.push(array[i].city);
} else {
array.splice(i, 1);
}
}
return array;
}
places = uniqueCity(places);
You could use a Map so the entries with the same key property (in your case 'city') only appear once
module.exports = (array, prop) => {
const keyValueArray = array.map(entry => [entry[prop], entry]);
const map = new Map(keyValueArray);
return Array.from(map.values());
};
More info about Map and array objects here
Basic example on Codepen
Another option:
const uniqueBy = prop => list => {
const uniques = {}
return list.reduce(
(result, item) => {
if (uniques[item[prop]]) return result
uniques[item[prop]] = item
return [...result, item]
},
[],
)
}
const uniqueById = uniqueBy('id')
uniqueById([
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
{ id: 1, name: 'one' },
{ id: 3, name: 'three' }
])
You can paste it on your console to see it working.
It should work for the scenario presented and a few others.
We can create the list of unique objects by any property using JavaScript Map.
For example :
var places = [{ 'lat': 12.123, 'lng': 13.213, 'city': "New York"},
{ 'lat': 3.123, 'lng': 2.213, 'city': "New York"},
{ 'lat': 43.123, 'lng': 12.213, 'city': "London"}];
var cityMap = new Map();
places.forEach(p=> cityMap.set(p.city, p));
console.log([...cityMap.values()]);
Execute code snippet to see the result.
As pointed out in the comments, you could use an object as a map, which will allow you to avoid duplicates, you can then enumerate the properties of the object.
working fiddle: http://jsfiddle.net/gPRPQ/1/
var places = [];
var a = {};
a.lat = 12.123;
a.lng = 13.213;
a.city = "New York";
places.push(a);
var b = {};
b.lat = 3.123;
b.lng = 2.213;
b.city = "New York";
places.push(b);
var unique = {}
for (var i = 0; i < places.length; i++) {
var place = places[i];
unique[place.city] = place;
}
for (var name in unique) {
var place = unique[name];
console.log(place);
}
var places = [];
var a = {};
a.lat = 12.123;
a.lng = 13.213;
a.city = "New York";
places.push(a);
var b = {};
b.lat = 3.123;
b.lng = 2.213;
b.city = "New York";
places.push(b);
getUniqAR(places,'city'); //Return Uniq Array by property
function getUniqAR(Data,filter){
var uniar =[];
Data.forEach(function(item,ind,arr){
var dupi=false;
if(!uniar.length) uniar.push(item) //push first obj into uniq array
uniar.forEach(function(item2, ind2,arr){
if(item2[filter] == item[filter]){ //check each obj prop of uniq array
dupi=true; //if values are same put duplicate is true
}
})
if(!dupi){ uniar.push(item)} //if no duplicate insert to uniq
})
console.log(uniar)
return uniar;
}
In simple Javascript code to remove duplicate cities from places array list is
var places = [{ 'lat': 12.123, 'lng': 13.213, 'city': "New York"},
{ 'lat': 3.123, 'lng': 2.213, 'city': "New York"},
{ 'lat': 43.123, 'lng': 12.213, 'city': "London"}];
var unique = [];
var tempArr = [];
places.forEach((value, index) => {
if (unique.indexOf(value.city) === -1) {
unique.push(value.city);
} else {
tempArr.push(index);
}
});
tempArr.reverse();
tempArr.forEach(ele => {
places.splice(ele, 1);
});
console.log(places);
Generic Typescript answer based on https://stackoverflow.com/a/18773857/49564 above:
export function isDistinct<T>(mapper: (value: T) => string): (value: T) => boolean {
const keys: { [index: string]: boolean } = {};
return (entry: T) => {
const key = mapper(entry);
if (keys[key] !== undefined) {
return false;
}
return keys[key] = true;
};
}
// Usage example:
const items = [ { id: 1 }, { id: 2 }, { id: 3 }, { id: 1 } ];
const unique = items.filter(isDistinct(i => i.id));
I think you want 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)
}
}
Another variation of the rafaelbiten approach:
const dedupExample = [
{id: 1, c: 'whatever'},
{id: 1, c: '1whatever'},
{id: 2, c: '2whatever'},
{id: 2, c: '2whatever'},
{id: 3, c: '2whatever'},
]
const getUniqueBy = (prop, list) => {
const objUniq = list.reduce((res, item) => ({ ...res, [item[prop]]: item }), {})
return Object.keys(objUniq).map(item => objUniq[item])
}
const uniq = getUniqueBy('id', dedupExample)
console.info('info', { uniq })
/* [
{id: 1, c: 'whatever'},
{id: 2, c: '2whatever'},
{id: 3, c: '2whatever'},
] */
const distinctArrayByCity= [
...new Map(array.map((item) => [item.city, item])).values(),
];
This thread may be old but thought I should share it. It is based on Pure JavaScript and removes Duplicate Objects based on the Properties Specified.
function removeDuplicates(originalArray, properties) {
var newArray = [];
var index = 0;
var lookupObject = {};
var totalProperties = properties.length;
for (var i = 0; i < originalArray.length; i++) {
var exists = false;
for (var a = 0; a < newArray.length; a++) {
var propsFound = 0;
for (var b = 0; b < totalProperties; b++) {
if (originalArray[i][properties[b]] == newArray[a][properties[b]]) {
propsFound++;
}
}
//If there is a match then break the for loop
if (propsFound == totalProperties) {
exists = true;
break;
}
} //End of New Array
if (!exists) {
newArray[index] = originalArray[i];
index++;
}
} //End of originalArray
return newArray;
}
You can view the fiddle here

Categories