I am looking for a way to take the following list:
directory = [
{
name: "Albert",
age: 40,
gender: "M"
},
{
name: "Suzanne",
age: 27,
gender: "F"
},
{
name: "Robert",
age: 19,
gender: "M"
},
{
name: "Connie",
age: 87,
gender: "F"
}
]
and make a dictionary on the key name
:
dictionary = {
"Albert": {
name: "Albert",
age: 40,
gender: "M"
},
"Suzanne": {
name: "Suzanne",
age: 27,
gender: "F"
},
"Robert": {
name: "Robert",
age: 19,
gender: "M"
},
"Connie": {
name: "Connie",
age: 87,
gender: "F"
}
}
This is similar to the C# ToDictionary
method. I know I could do something like iterate over directory
in a for
loop or .each
call, and modify the value of dictionary
each iteration. I would prefer, however, to make a functional programming-like assignment instead, e.g.
dictionary = directory.toDictionary(p => p.name);
Does such a method exist, say within ES6 or lodash?
I am looking for a way to take the following list:
directory = [
{
name: "Albert",
age: 40,
gender: "M"
},
{
name: "Suzanne",
age: 27,
gender: "F"
},
{
name: "Robert",
age: 19,
gender: "M"
},
{
name: "Connie",
age: 87,
gender: "F"
}
]
and make a dictionary on the key name
:
dictionary = {
"Albert": {
name: "Albert",
age: 40,
gender: "M"
},
"Suzanne": {
name: "Suzanne",
age: 27,
gender: "F"
},
"Robert": {
name: "Robert",
age: 19,
gender: "M"
},
"Connie": {
name: "Connie",
age: 87,
gender: "F"
}
}
This is similar to the C# ToDictionary
method. I know I could do something like iterate over directory
in a for
loop or .each
call, and modify the value of dictionary
each iteration. I would prefer, however, to make a functional programming-like assignment instead, e.g.
dictionary = directory.toDictionary(p => p.name);
Does such a method exist, say within ES6 or lodash?
Share Improve this question edited Jul 17, 2019 at 20:48 Servy 204k27 gold badges347 silver badges465 bronze badges asked Jul 17, 2019 at 20:37 BondolinBondolin 3,1217 gold badges40 silver badges68 bronze badges 6 | Show 1 more comment6 Answers
Reset to default 6Javascript's Map
closely resembles a dictionary. Its instances sport handy has
, get
, set
, keys
, values
methods. It's also directly iterable via a forEach
method.
You can construct a Map
using an array of "key-value-pairs". (in quotes, because in reality we're using arrays in some tuple-like fashion).
To create a Map
to use as a dictionary, you'd do:
const directory=[{name:"Albert",age:40,gender:"M"},{name:"Suzanne",age:27,gender:"F"},{name:"Robert",age:19,gender:"M"},{name:"Connie",age:87,gender:"F"}];
const myDict = new Map(
directory.map(p => [p.name, p])
);
console.log("Has Albert:", myDict.has("Albert"))
myDict.forEach(p => { console.log(`${p.name} has age ${p.age}`) });
You could map key and value and create an object from this arrays with (upcoming) Object.fromEntries
.
var directory = [{ name: "Albert", age: 40, gender: "M" }, { name: "Suzanne", age: 27, gender: "F" }, { name: "Robert", age: 19, gender: "M" }, { name: "Connie", age: 87, gender: "F" }],
result = Object.fromEntries(directory.map(o => [o.name, o]));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A classic approach
var directory = [{ name: "Albert", age: 40, gender: "M" }, { name: "Suzanne", age: 27, gender: "F" }, { name: "Robert", age: 19, gender: "M" }, { name: "Connie", age: 87, gender: "F" }],
result = Object.assign({}, ...directory.map(o => ({ [o.name]: o })));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
There's no specific built-in but it's easy with .reduce()
:
let dict = directory.reduce((d, v) => (d[v.name] = v, d), {});
As pointed out in a good comment on the question, one thing you need to consider is the meaning of duplicate names. Is that a "bad data" situation? Should there be an array of names for such duplicates? Questions like that are specific the the data structure and its purpose in your specific application.
Since you asked by lodash
solution too, you can use the .keyBy() method of that library.
const directory = [{name:"Albert",age:40,gender:"M"},{name:"Suzanne",age:27,gender:"F"},{name:"Robert",age:19,gender:"M"},{name:"Connie",age:87,gender:"F"}];
console.log(_.keyBy(directory, o => o.name));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
However, as I say on a commentary, the first structure allows more than one entry for the same name, while the resulting one don't. So, in the case that the directory
array includes multiple objects with the same name, you will only get the last of they on the resulting data.
You could reduce
the array. Use Object.assign()
to add each name as a property
const directory=[{name:"Albert",age:40,gender:"M"},{name:"Suzanne",age:27,gender:"F"},{name:"Robert",age:19,gender:"M"},{name:"Connie",age:87,gender:"F"}],
output = directory.reduce((r, o) => Object.assign(r, { [o.name]: o }), {})
console.log(output)
Here is how I added an implementation of ToDictionary which allows you to define a key selector also and allow duplicate values per key which will be saved into an array if there are many values. The method is part of version 1.0.14 of my library "simpletslinq" hosted on Npmjs.com
Note that my code is written in Typescript, so you must run tsc typescript compiler on it to create a true Javscript equivalent. In its core, we are using the map function to create the dictionary. The added typescript stuff is to add type checking..
if (!Array.prototype.ToDictionary) {
Array.prototype.ToDictionary = function <T>(keySelector: (arg: T) => any): any {
let hash = {};
this.map(item => {
let key = keySelector(item);
if (!(key in hash)) {
hash[key] = item;
}
else {
if (!(Array.isArray(hash[key]))) {
hash[key] = [hash[key]];
}
hash[key].push(item);
}
});
return hash;
}
}
A spec (test) for this code then looks like this:
it('can apply method ToDictionary on an array, allowing specificaton of a key selector for the dictionary object', () => {
let heroes = [{ name: "Han Solo", age: 47, gender: "M" }, { name: "Leia", age: 29, gender: "F" }, { name: "Luke", age: 24, gender: "M" }, { name: "Lando", age: 47, gender: "M" }];
let dictionaryOfHeroes = heroes.ToDictionary<Hero>(x => x.gender);
let expectedDictionary = {
"F": {
name: "Leia", age: 29, gender: "F"
},
"M": [
{ name: "Han Solo", age: 47, gender: "M" },
{ name: "Luke", age: 24, gender: "M" },
{ name: "Lando", age: 47, gender: "M" }
]
};
expect(dictionaryOfHeroes).toEqual(expectedDictionary);
});
.reduce()
. – Pointy Commented Jul 17, 2019 at 20:38toDictionary
method. – Kenneth K. Commented Jul 17, 2019 at 20:44for(let i=0; i<array.length; ++i)
loop is still the fastest way to iterate an Array. – Thomas Commented Jul 17, 2019 at 22:53