Set and spread operator [duplicate] - javascript

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

Related

pushing into an array inside an object using function it's returning Number instead of the value of pushed array [duplicate]

Are there any substantial reasons why modifying Array.push() to return the object pushed rather than the length of the new array might be a bad idea?
I don't know if this has already been proposed or asked before; Google searches returned only a myriad number of questions related to the current functionality of Array.push().
Here's an example implementation of this functionality, feel free to correct it:
;(function() {
var _push = Array.prototype.push;
Array.prototype.push = function() {
return this[_push.apply(this, arguments) - 1];
}
}());
You would then be able to do something like this:
var someArray = [],
value = "hello world";
function someFunction(value, obj) {
obj["someKey"] = value;
}
someFunction(value, someArray.push({}));
Where someFunction modifies the object passed in as the second parameter, for example. Now the contents of someArray are [{"someKey": "hello world"}].
Are there any drawbacks to this approach?
See my detailed answer here
TLDR;
You can get the return value of the mutated array, when you instead add an element using array.concat[].
concat is a way of "adding" or "joining" two arrays together. The awesome thing about this method, is that it has a return value of the resultant array, so it can be chained.
newArray = oldArray.concat[newItem];
This also allows you to chain functions together
updatedArray = oldArray.filter((item) => {
item.id !== updatedItem.id).concat[updatedItem]};
Where item = {id: someID, value: someUpdatedValue}
The main thing to notice is, that you need to pass an array to concat.
So make sure that you put your value to be "pushed" inside a couple of square brackets, and you're good to go.
This will give you the functionality you expected from push()
You can use the + operator to "add" two arrays together, or by passing the arrays to join as parameters to concat().
let arrayAB = arrayA + arrayB;
let arrayCD = concat(arrayC, arrayD);
Note that by using the concat method, you can take advantage of "chaining" commands before and after concat.
Are there any substantial reasons why modifying Array.push() to return the object pushed rather than the length of the new array might be a bad idea?
Of course there is one: Other code will expect Array::push to behave as defined in the specification, i.e. to return the new length. And other developers will find your code incomprehensible if you did redefine builtin functions to behave unexpectedly.
At least choose a different name for the method.
You would then be able to do something like this: someFunction(value, someArray.push({}));
Uh, what? Yeah, my second point already strikes :-)
However, even if you didn't use push this does not get across what you want to do. The composition that you should express is "add an object which consist of a key and a value to an array". With a more functional style, let someFunction return this object, and you can write
var someArray = [],
value = "hello world";
function someFunction(value, obj) {
obj["someKey"] = value;
return obj;
}
someArray.push(someFunction(value, {}));
Just as a historical note -- There was an older version of JavaScript -- JavaScript version 1.2 -- that handled a number of array functions quite differently.
In particular to this question, Array.push did return the item, not the length of the array.
That said, 1.2 has been not been used for decades now -- but some very old references might still refer to this behavior.
http://web.archive.org/web/20010408055419/developer.netscape.com/docs/manuals/communicator/jsguide/js1_2.htm
By the coming of ES6, it is recommended to extend array class in the proper way , then , override push method :
class XArray extends Array {
push() {
super.push(...arguments);
return (arguments.length === 1) ? arguments[0] : arguments;
}
}
//---- Application
let list = [1, 3, 7,5];
list = new XArray(...list);
console.log(
'Push one item : ',list.push(4)
);
console.log(
'Push multi-items :', list.push(-9, 2)
);
console.log(
'Check length :' , list.length
)
Method push() returns the last element added, which makes it very inconvenient when creating short functions/reducers. Also, push() - is a rather archaic stuff in JS. On ahother hand we have spread operator [...] which is faster and does what you needs: it exactly returns an array.
// to concat arrays
const a = [1,2,3];
const b = [...a, 4, 5];
console.log(b) // [1, 2, 3, 4, 5];
// to concat and get a length
const arrA = [1,2,3,4,5];
const arrB = [6,7,8];
console.log([0, ...arrA, ...arrB, 9].length); // 10
// to reduce
const arr = ["red", "green", "blue"];
const liArr = arr.reduce( (acc,cur) => [...acc, `<li style='color:${cur}'>${cur}</li>`],[]);
console.log(liArr);
//[ "<li style='color:red'>red</li>",
//"<li style='color:green'>green</li>",
//"<li style='color:blue'>blue</li>" ]
var arr = [];
var element = Math.random();
assert(element === arr[arr.push(element)-1]);
How about doing someArray[someArray.length]={} instead of someArray.push({})? The value of an assignment is the value being assigned.
var someArray = [],
value = "hello world";
function someFunction(value, obj) {
obj["someKey"] = value;
}
someFunction(value, someArray[someArray.length]={});
console.log(someArray)

JS do not sort numeral object indices

When creating an object like so
let o = {}
o["prop2"] = "val"
o["prop1"] = "val"
The properties are stored in the order they are set, so when getting the keys of the object (Object.keys(o)) they are returned in their proper order (["prop2", "prop1"]). This order has wide reaching consequences as for...in loops use it and hence JSON.stringify is influenced by it as well. I want to use this behaviour for performance optimisations (like sending already sorted objects that are easier to parse).
However all this falls apart when using numeric indices. The seemingly similar object
let o = {}
o["2"] = "val"
o["1"] = "val"
does not confirm the behaviour above. Here Object.keys(o) returns ["1", "2"] :(.
Can this be avoided somehow? Without pre-/post fixing all properties?
Use a Map instead to retain insertion order.
let m = new Map;
m.set("2", "val");
m.set("1", "val");
m.forEach((v,k)=>console.log(k,"=",v));
To convert it to JSON, you can stringify the entries of the Map. You can use parse the string and pass the entries to the constructor of Map to convert it back.
const map = new Map([
['foo', 10],
['baz', 42]
]);
let str = JSON.stringify([...map.entries()])
console.log(str);
new Map(JSON.parse(str)).forEach((v,k)=>console.log(k,"=",v));
You can provide an callback to JSON.stringify and JSON.parse to convert nested Map structures.
let m = new Map;
m.set("b", new Map([["c","d"],["e","f"], ["d", new Map([["a", 1]])]]));
let str = JSON.stringify(m, (key,val)=>{
return Object.prototype.toString.call(val) === '[object Map]' ? [...val.entries()] : val;
});
console.log(str);
let m2 = JSON.parse(str, (key, val)=>{
return Array.isArray(val) && Array.isArray(val[0]) ? new Map(val) : val;
});
console.log(m2);
Demo: https://jsfiddle.net/ob8d2uLf/1/

Javascript or Query - Convert Associative Array into Object

This is a simple question, not an expert in JS by any means and searched around but couldn't get any existing examples on Stackoverflow to work.
Basically I have a associative array and I need to convert it into an object:
Example:
var combinedproducts = [["Testing-1","test-1"],["Testing-2","test2"],["Testing-3","test3"]]
Need it to become this:
var products = {
// 'productid1':'Product Description One',
'Testing-1':'test-1',
'Testing-2':'test-2',
'Testing-3':'test-3'
};
What's the best / simplest way to do this? I can use regular javascript or jquery.
Thanks for the help!
If you don't have particular support issues, you can simply use Object.fromEntries, assuming "test2" and "test3" are actually array typos rather than intended to be transformed to test-2 and test-3 respectively as in your sample output.
Otherwise, you need to apply transformation to them (in that case, either reduce, a simple for or a foreach or even map can accomplish that).
Beware that, as mentioned above, Object.entries has not the same support as other solutions. For instance, keep in mind that it WON'T work in IE and Edge in general, check this link for further compatibility informations.
var combinedproducts = [["Testing-1","test-1"],["Testing-2","test2"],["Testing-3","test3"]];
const products = Object.fromEntries(combinedproducts);
console.log(products);
You can do it with .reduce() on your source array:
let object = combinedProducts.reduce((o, a) => (
o[a[0]] = a[1],
o
), {});
For what it's worth, the source array isn't really an "associative array" in any formal sense. JavaScript doesn't have an associative array type.
You can use array reduce. Inside the callback function add the first element of the inner array as key and the second element as the value
let data = [
["Testing-1", "test-1"],
["Testing-2", "test2"],
["Testing-3", "test3"]
];
let newData = data.reduce((acc, curr) => {
acc[curr[0]] = curr[1]
return acc;
}, {});
console.log(newData)
you can also use this method
const c = [["Testing-1","test-1"],["Testing-2","test2"],["Testing-3","test3"]];
obj = c.reduce((acc, [ key, val ]) => Object.assign(acc, { [key]: val }), {});
console.log(obj)
One more alternate way doing in one line with Object.assign and map.
var combinedproducts = [
["Testing-1", "test-1"],
["Testing-2", "test2"],
["Testing-3", "test3"]
];
const obj = Object.assign(
{},
...combinedproducts.map(([key, value]) => ({ [key]: value }))
);
console.log(obj);

Array.reduce() on an array of objects -- getting back strings of single letters

I'm working to understand Array.reduce() in JavaScript. I have an array of objects that I'm trying to apply .reduce() to, but I'm getting back an array of single letter strings.
Goal:
["Stuff", "necklace", "ring", "bracelet"]
Current Array of Objects
const productArray =
[
{
id: 1,
productTitle: "Necklace"
},
{
id: 2,
productTitle: "Ring"
},
{
id: 3,
productTitle: "Bracelet"
}
]
Function call
const newStuff = productArray.reduce(function(a, currentValue) {
return [...a, ...currentValue.productTitle];
}, ["Stuff"])
Actual result:
What do I need to do to specify that I don't want "productTitle" broken down into single-letter strings? I have been looking for resources regarding .reduce() on an array of objects but I haven't found anything very helpful. Any pointers?
To concatenate an array and value when using spread to create a new array, you spread the previous array to the new array, and add the new item without spreading it.
const productArray = [{"id":1,"productTitle":"Necklace"},{"id":2,"productTitle":"Ring"},{"id":3,"productTitle":"Bracelet"}];
const newStuff = productArray.reduce((a, currentValue) =>
[...a, currentValue.productTitle], []);
console.log(newStuff);
In this case, it's better to use Array.map():
const productArray = [{"id":1,"productTitle":"Necklace"},{"id":2,"productTitle":"Ring"},{"id":3,"productTitle":"Bracelet"}];
const newStuff = productArray.map((currentValue) => currentValue.productTitle);
console.log(newStuff);
Do not spread the title, pass it as it is:
const newStuff = productArray.reduce(function(a, currentValue) {
return [...a, currentValue.productTitle];
}, ["Stuff"]);
...currentValue.productTitle spreads into an array of individual letters, you only want to spread a variable, the aggregate here.
Basically a string is iterable, because the iterator is implemented and returns an array of single characters, if using spread syntax ....
Spread syntax allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected.
console.log([...'foo']);
Other answers have pointed out why your code is wrong. But I do want to also note that what you're doing is already covered by Array.prototype.concat:
const productArray = [{"id":1,"productTitle":"Necklace"},{"id":2,"productTitle":"Ring"},{"id":3,"productTitle":"Bracelet"}];
const newStuff = productArray.reduce((a, val) => a.concat(val.productTitle), ['Struff']);
console.log(newStuff);
(And of course, as another answer has mentioned, this sounds more like a use for map than reduce, which might not matter since you're using this to learn reduce.)
The use of spread in this case is unnecessary and inefficient as it creates a new accumulator array from the previous one on every iteration. You can remove spread (and fix your issue) and use concat instead to keep it as a one-liner.
However, since you're just adding one new value on each iteration, you should use push. It requires one more line of code but is likely more efficient than using concat.
var productArray = [{id: 1,productTitle: "Necklace"},
{id: 2,productTitle: "Ring"},
{id: 3,productTitle: "Bracelet"}
];
// Using concat
var newStuff = productArray.reduce((acc, value) =>
acc.concat(value.productTitle),
["Stuff"]);
console.log(newStuff);
// Using push
var newStuff = productArray.reduce((acc, value) => {
acc.push(value.productTitle);
return acc;
}, ["Stuff"]);
console.log(newStuff);

es6 unique array of objects with set

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'}]

Categories

Resources