es6 unique array of objects with set - javascript

I came across this example for creating unique arrays with es6
[ ...new Set(array) ]
Which seems to work fine until I tried it with an array of objects and it didn't return unique array.
i.e.
let item = [ ...new Set([{id:123,value:'test'},{id:123,value:'test'}]) ];
Why is that ?

you can try to do
uniqueArray = a => [...new Set(a.map(o => JSON.stringify(o)))].map(s => JSON.parse(s))
I know its ugly as hell but in most cases works apart from where you have new Date() in your object param then that on stringify be converted to ISO string.
so then do
let arr = [{id:1},{id:1},{id:2}];
uniqueArray(arr) //[{id:1},{id:2}]

Why is that ?
As per documentation
The Set object lets you store unique values of any type, whether
primitive values or object references.
Now reference for each of those arrays inside that Set constructor will be different so they are not considered to be a unique value by the constructor.

This will work:
let objectReference = {id:123,value:'test'}
let uniqueArray = [...new Set([objectReference, objectReference])]
>> [{id:123,value:'test'}]
What you're doing:
let objRef1 = {id:123,value:'test'} // creates a reference to a location in memory
let objRef2 = {id:123,value:'test'} // creates a new reference to a different place in memory
let uniqueArray = [...new Set([objRef1, objRef2])]
>> [{id:123,value:'test'},{id:123,value:'test'}]

Related

Set and spread operator [duplicate]

Set seems like a nice way to create Arrays with guaranteed unique elements, but it does not expose any good way to get properties, except for generator [Set].values, which is called in an awkward way of mySet.values.next().
This would have been ok, if you could call map and similar functions on Sets. But you cannot do that, as well.
I've tried Array.from, but seems to be converting only array-like (NodeList and TypedArrays ?) objects to Array. Another try: Object.keys does not work for Sets, and Set.prototype does not have similar static method.
So, the question: Is there any convenient inbuilt method for creating an Array with values of a given Set ? (Order of element does not really matter).
if no such option exists, then maybe there is a nice idiomatic one-liner for doing that ? like, using for...of, or similar ?
if no such option exists, then maybe there is a nice idiomatic
one-liner for doing that ? like, using for...of, or similar ?
Indeed, there are several ways to convert a Set to an Array:
Using Array.from:
Note: safer for TypeScript.
const array = Array.from(mySet);
Simply spreading the Set out in an array:
Note: Spreading a Set has issues when compiled with TypeScript (See issue #8856). It's safer to use Array.from above instead.
const array = [...mySet];
The old-fashioned way, iterating and pushing to a new array (Sets do have forEach):
const array = [];
mySet.forEach(v => array.push(v));
Previously, using the non-standard, and now deprecated array comprehension syntax:
const array = [v for (v of mySet)];
via https://speakerdeck.com/anguscroll/es6-uncensored by Angus Croll
It turns out, we can use spread operator:
var myArr = [...mySet];
Or, alternatively, use Array.from:
var myArr = Array.from(mySet);
Assuming you are just using Set temporarily to get unique values in an array and then converting back to an Array, try using this:
_.uniq([])
This relies on using underscore or lo-dash.
Perhaps to late to the party, but you could just do the following:
const set = new Set(['a', 'b']);
const values = set.values();
const array = Array.from(values);
This should work without problems in browsers that have support for ES6 or if you have a shim that correctly polyfills the above functionality.
Edit: Today you can just use what #c69 suggests:
const set = new Set(['a', 'b']);
const array = [...set]; // or Array.from(set)
Use spread Operator to get your desired result
var arrayFromSet = [...set];
The code below creates a set from an array and then, using the ... operator.
var arr=[1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,];
var set=new Set(arr);
let setarr=[...set];
console.log(setarr);
SIMPLEST ANSWER
just spread the set inside []
let mySet = new Set()
mySet.add(1)
mySet.add(5)
mySet.add(5)
let arr = [...mySet ]
Result: [1,5]
In my case the solution was:
var testSet = new Set();
var testArray = [];
testSet.add("1");
testSet.add("2");
testSet.add("2"); // duplicate item
testSet.add("3");
var someFunction = function (value1, value2, setItself) {
testArray.push(value1);
};
testSet.forEach(someFunction);
console.log("testArray: " + testArray);
value1 equals value2 => The value contained in the the current position in the Set. The same value is passed for both arguments
Worked under IE11.
Using Set and converting it to an array is very similar to copying an Array...
So you can use the same methods for copying an array which is very easy in ES6
For example, you can use ...
Imagine you have this Set below:
const a = new Set(["Alireza", "Dezfoolian", "is", "a", "developer"]);
You can simply convert it using:
const b = [...a];
and the result is:
["Alireza", "Dezfoolian", "is", "a", "developer"]
An array and now you can use all methods that you can use for an array...
Other common ways of doing it:
const b = Array.from(a);
or using loops like:
const b = [];
a.forEach(v => b.push(v));
the simplistic way to doing this
const array = [...new Set([1,1,2,3,3,4,5])]
console.log(array)
Here is an easy way to get only unique raw values from array. If you convert the array to Set and after this, do the conversion from Set to array. This conversion works only for raw values, for objects in the array it is not valid. Try it by yourself.
let myObj1 = {
name: "Dany",
age: 35,
address: "str. My street N5"
}
let myObj2 = {
name: "Dany",
age: 35,
address: "str. My street N5"
}
var myArray = [55, 44, 65, myObj1, 44, myObj2, 15, 25, 65, 30];
console.log(myArray);
var mySet = new Set(myArray);
console.log(mySet);
console.log(mySet.size === myArray.length);// !! The size differs because Set has only unique items
let uniqueArray = [...mySet];
console.log(uniqueArray);
// Here you will see your new array have only unique elements with raw
// values. The objects are not filtered as unique values by Set.
// Try it by yourself.
I would prefer to start with removing duplications from an array and then try to sort.
Return the 1st element from new array.
function processData(myArray) {
var s = new Set(myArray);
var arr = [...s];
return arr.sort((a,b) => b-a)[1];
}
console.log(processData([2,3,6,6,5]);
function countUniqueValues(arr) {
return Array.from(new Set(arr)).length
}
console.log(countUniqueValues([1, 2, 3, 4, 4, 4, 7, 7, 12, 12, 13]))

Does Javascript Array.map() return a different instance object?

If I use Array.map() to replace each null element with an instance of object like this.
let arr = new Array(10).fill(null).map(() => new LinkedList());
Does this replace each element with the same referenced LinkedList() instance or different reference?
You will get 10 different instance of new LinkedList().
You could verify that by checking console.log(arr[0] === arr[1]).
Usually a map function will pay attention to the existing value in the array it is being used to process so it has to be called for each item in the array and will generate a new value for each item.
const double = value => 2 * value;
const doubled = [1, 2, 3].map(double);
console.log(doubled);
It isn't called once and the value used for every position.
You'll get a new instance for each position because the function will be called each time.
You can trivially test this with a comparison:
function LinkedList() {};
let arr = new Array(10).fill(null).map(() => new LinkedList());
console.log(arr);
console.log(arr[0] === arr[1]);
It should be different References.

filter values from an array of strings from another array of objects

I am trying to get only those strings which are not present in array of objects
var objects = [{name:'a',is:false},{name:'b',is:false},{name:'c',is:false}];
var strings = ['a','b','z','x'];
let result = strings.filter(o1 => !objects.includes(o2=> o2.name === o1));
console.log(result)
so the result should be only 'z' and 'x'
From your objects array, you can create Map (lookup) which has keys as the name property and values as the negated version of the is property.
Map {
'a': true,
'b': true,
'c': true
}
You can then use this to lookup to filter your array of strings. If the Map has a value which is true then you can remove it from your array (by returning false), otherwise, if the Map doesn't have the current string, you can keep it in the array. Creating a map/object by preprocessing your objects array saves you from needing to use an inner loop inside of your filter(), improve the overall efficiency of your code if your array sizes increase.
const objects = [{name:'a',is:false},{name:'b',is:false},{name:'c',is:false}];
const strings = ['a','b','z','x'];
const lookup = new Map(objects.map(({name, is}) => [name, !is]));
const result = strings.filter(v => !lookup.get(v));
console.log(result);
Your approach was very close. However, the .includes() method doesn't accept a callback function like you're providing it with (.includes() will search your array for that same function object references your providing it with)
const cb = v => v; // fake callback
const arr = [cb]; // some array contains that callback
console.log(arr.includes(cb)); // providing the callback as a function to includes
// => true, as the `cb` function is inside of `arr`
If you want to provide your own "includes" logic, you can use .some() instead:
var objects = [{name:'a',is:false},{name:'b',is:false},{name:'c',is:false}];
var strings = ['a','b','z','x'];
let result = strings.filter(o1 => !objects.some(o2 => o2.name === o1));
console.log(result)
If you're curious, the above logic with .some() has a time complexity of O(N*M), whereas the Map approach has a time complexity of O(N+M), where N is the size of your strings array and M is the size of your objects array.
Used map to simplify the array and then used filter and includes to find out the strings which are not part of objects.
var objects = [{name:'a',is:false},{name:'b',is:false},{name:'c',is:false}];
var strings = ['a','b','z','x'];
let names = objects.map(dataItem => dataItem.name)
let filteredNames = strings.filter(stringItem => !names.includes(stringItem))
console.log(filteredNames)
includes now allow callback function.
instead of this use find:
var objects = [{name:'a',is:false},{name:'b',is:false},{name:'c',is:false}];
var strings = ['a','b','z','x'];
let result = strings.filter(o1 => !objects.find(o2=> o2.name === o1));
console.log(result)

Lodash how to assign specific property of object during comparing two collections of different objects

I have one question. Is there any function in lodash library which is going to provide me the method for comparing each object in both collections by specific property and if the condition is fulfilled then creating another object?
example:
a) [{a:1,b:'abc',c:'dfr'},{a:3,b:'dfe',c:'gty'}....{}]
b) [{a:3,b:'fgt',d:'ghr'},{a:5,b:'ghk',d:'bhj'}...{}]
result:[{a:3,b:'dfe',c:'gty',d:'ghr'}]
I would like to compare these two collections by 'a' parameter and if a parameter is matched then assign parameter 'd' to object from the collection a). I have read something about differenceWith or intersection, but I am not sure if it may work, or maybe there are better functions to do this. Thanks in advance!
Create a Map of the items in array2 by their keys. Iterate array1 with Array.filter(), and remove all items that their a property is not found in the Map. Use Array.map() to combine the remaining items with d property of their counterpart in the Map:
const array1 = [{a:1,b:'abc',c:'dfr'},{a:3,b:'dfe',c:'gty'}];
const array2 = [{a:3,b:'fgt',d:'ghr'},{a:5,b:'ghk',d:'bhj'}];
// create a Map of array2 items by the a property
const array2Map = new Map(array2.map((o) => [o.a, o]));
const result = array1
// filter out all items that don't have matching item in the Map
.filter(o => array2Map.has(o.a))
// map the items, and add the d property from the item in the Map
.map(o => ({
...o,
d: array2Map.get(o.a).d
}));
console.log(result);
1) Native way: doing array map and comparing inside map loop with assigning objects.
const array1 = [{a:1,b:'abc',c:'dfr'},{a:3,b:'dfe',c:'gty'}];
const array2 = [{a:3,b:'fgt',d:'ghr'},{a:5,b:'ghk',d:'bhj'}];
const result = array1.map(obj1 => {
const obj2 = array2.find(obj => obj.a === obj1.a);
if (obj2) return Object.assign({}, obj1, {d: obj2.d});
}).filter(value => value);
console.log(result);
2) Lodash way: same as in example 1 but using only lodash methods
const array1 = [{a:1,b:'abc',c:'dfr'},{a:3,b:'dfe',c:'gty'}];
const array2 = [{a:3,b:'fgt',d:'ghr'},{a:5,b:'ghk',d:'bhj'}];
const result = _.compact(
_.map(array1, obj1 => {
const obj2 = _.find(array2, _.matchesProperty('a', obj1.a));
if (obj2) return _.assign({}, obj1, _.pick(obj2, ['d']));
})
);
console.log(result);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.10/lodash.min.js"></script>
P.S. Try to avoid lodash, underscore and etc stuff as much as possible. JS, ES are rich enough.

Pass array of objects by reference and save the actual array in JavaScript

I am passing an array to a function. Since, in javascript,
arrays by default get passed by reference I tried to make a copy of array. But in the end, the operations performed in function effects the actual array data.
All I want to do is save the actual state of an array.
Here is my code :
let arrcopy =new Array( items.dt);
citem = binarySearch(arrcopy[0], ind.ItemID);
You means you want to preserve the original array as it is (you want to preserve immutability)?
Then use reduce method.
const test = (arr) => {
return arr.reduce((result, val) => {
// do the needful here
result.push(val);
return result;
}, []);
}
expect(arr).to.be.equal(result); => will return false as the original array won't be updated
You will need to create array as copy with Object.assign
let arrcopy = Object.assign([],items.dt);
citem = binarySearch(arrcopy[0], ind.ItemID);
Or just ES6 way with destruction/spread operator is enough
let arrcopy = [...items.dt];
JSON.parse(JSON.stingify(items.dt))
this has made my work

Categories

Resources