Javascript or Query - Convert Associative Array into Object - javascript

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

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

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

How to avoid "Arrow function expect to return a value" error in Airbnb ESLint

I am running eslint and it is recommended to return a value whenever an arrow function(lambda function) is used. Well that makes sense. However, I come across a case that is hard to walk around.
Dict = {}
Instances = [/* an array of items where items is a dictionary that contains data */]
Instances.map((item) => {
Dict[item.name] = item.url;
});
My goal is to get the data from the Instances array and fill the dictionary Dict with it. I am using the array function to assign key value pair to the dictionary, but that violates the rule of the arrow function.
Is there any iteratools or functions other than map that would help me to achieve the goal, and avoid the rule violation?
Edit: This does not adhere to Airbnb's ES6 Style Guide.
My goal is to get the data from the Instances array and fill the dictionary with it.
Use .reduce
.. and just pass an empty object as the accumulator, filling it up as you iterate through your array.
const instances = [
{ name: 'foo', url: 'https://google.com' },
{ name: 'bar', url: 'https://stackoverflow.com' }
]
const result = instances.reduce((dict, item) => {
dict[item.name] = item.url
return dict
}, {})
console.log(result)
Why not .map?
Array.map always returns a new Array and is meant for mapping each array element to another format.
If your resulting data structure shouldn't be an Array, with the same length as the Array you are operating on, you should avoid using it.
Why .reduce instead of .forEach?
I use forEach only for doing "work" rather than transforming data. Transforming data is almost always achievable with just map and/or reduce.
Here's what I mean by "work":
const users = [userInstance, userInstance, userInstance]
users.forEach(user => user.sendEmail('Hello World'))
Use forEach instead of map.
The point of map is to modify each item in an array and put the modified versions in a new array.
forEach just runs a function on each item.
If you are looking for ES6 solution to fill dictionary object this could help and should pass ESLint also:-
const dict = Instances.reduce((map, obj) => (map[obj.name] = obj.url, map), {});
update
const dict = Instances.reduce((map, obj) => {
let mapClone = {};
mapClone = Object.assign({}, map);
mapClone[obj.name] = obj.url;
return mapClone;
}, {});

Union arrays from all object properties

Lets say I have an object:
let obj={
hash1:{
images:[img1,img2....]
}
hash2:{
images:[img100,img200....]
}
hash3:{
images:[img1000,img2000....]
}
...
}
I want to union all those arrays in one array.
I understand that I can use next code:
let unionArray=[];
Object.values(obj).forEach((item)=>{
unionArray=unionArray.concat(item.images)
});
Are there more elegant way to do such task e.g with some framework or in one line coding.
An array reduction will do pretty nicely here:
let union = Object.values(obj).reduce((c, i) => c.concat(i.images), []);
You can use map() on Object.keys() and ES6 spread syntax.
let obj = {"hash1":{"images":["img1","img2"]},"hash2":{"images":["img100","img200"]},"hash3":{"images":["img1000","img2000"]}}
var arr = [].concat(...Object.keys(obj).map(e => obj[e].images));
console.log(arr)
Either use Array#map.
const union = [].concat(...Object.values(obj).map(v => v.images));

A more elegant way to write this .map call?

I have some code that essentially takes an array of objects and just adds an additional key to each item. I want to be able to express this as tersely as possible as an experiment.
let fruits = [
{"type" : "orange"},
{"type" : "apple"},
{"type" : "banana"}
];
console.log(fruits.map((fruit) => {
fruit.price = "$1.00";
return fruit;
}));
Currently, this works, but it's certainly no one liner and the return statement is still in there, and I feel like there's a way to get rid of it given the fat arrow syntax.
One approach would be to use Object.assign to extend the object and also return the resulting newly-created object:
console.log(fruits.map(fruit => Object.assign(fruit, { price: "1.00" })));
Babel REPL Example
This removes the need for the return keyword, but it's hardly the biggest space-saver. It is also equivalent to what you already have (in that the original fruit object is modified. As joews points out below, if you wanted to leave the original array in-tact you can use an empty target object like so:
Object.assign({}, fruit, { price: "1.00"});
This will ensure that your original array is unmodified (which may or may not be what you want).
Finally, combining this with the spread operator gives us:
console.log(fruits.map(fruit => ({...fruit, price: "1.00" })));
You can also use .forEach() instead of .map() to directly modify fruits if you don't need the original version of fruits
fruits.forEach((fruit) => fruit.price = "$1.00");
http://www.es6fiddle.net/igwdk0gk/
Could do something like this, not recommended for readibility but technically one line.
fruits.map(fruit => (fruit.price = "$1.00") && fruit);
As others have mentioned this method just adds a property to the object and does not copy it. A simple way to keep this as a one liner, use a map and actually create a copy would be:
fruits.map(fruit => Object.assign({price: "$1.00"}, fruit));
Object.assign() will assign all the properties of fruit to the object { price: "$1.00" } and return it.
Live example:
"use strict";
let log = function() {
output.textContent += [].join.call(arguments, ' ') + '\n\n';
};
log('# MAP (OR FOREACH) WITHOUT ASSIGN');
let fruits = [
{"type" : "orange"},
{"type" : "apple"},
{"type" : "banana"}
];
let newfruits = fruits.map(fruit => (fruit.price = "$1.00") && fruit);
log('fruits', JSON.stringify(fruits));
log('newfruits', JSON.stringify(newfruits));
log('^-- Both are modified since newfruits its a new array with the same objects');
log('# MAP WITH ASSIGN');
fruits = [
{"type" : "orange"},
{"type" : "apple"},
{"type" : "banana"}
];
newfruits = fruits.map(fruit => Object.assign({price: "$1.00"}, fruit));
log('fruits', JSON.stringify(fruits));
log('newfruits', JSON.stringify(newfruits));
log('^-- Only newfruits is modified since its a new array with new objects');
pre {
word-wrap: break-word;
}
<pre id="output"></pre>
There are many ways to do this:
Side effects with the comma operator
If you want to do it inline you can use the comma operator (though it's a little obscure):
fruits.map((fruit) => (fruit.price = "$1.00", fruit))
We could also use && since assignment returns the assigned value and "$1.00" is truthy but the comma operator is more general, since we can also set false or 0 and have everything continue to work.
Higher-ordered functions
It's probably better to make a helper function, however:
// We're currying manually here, but you could also make the signature
// setter(name, value) and use your function of choice to curry when you need to.
function setter(name) {
return (value) => (obj) => {
obj[name] = value;
return obj;
}
}
Then you can use:
fruits.map(setter("price")("$1.00"))
Embrace mutability
As #Suppen points out in their comment, because normal JavaScript objects are mutable you can also avoid the map and use forEach instead:
fruits.forEach(fruit => fruit.price = "$1.00");
// Each element in fruits has been modified in-place.
A mapping function should almost always be pure. If you are only going to modify the objects, a simple loop will do better (for (let fruit of fruits) fruit.price = …; console.log(fruits);).
So when you're returning a new object, a one-liner will be easy:
console.log(fruits.map(({type}) => ({type, price:"$1.00"})));
If you've got many properties, or properties you don't know, then Object.assign({}, …) is your friend (as in #joews' comment to #RGraham's answer).

Categories

Resources