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, eg
dictionary = directory.toDictionary(p => p.name);
Does such a method exist, say within ES6 or lodash?
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; }
Javascript'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 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)
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/lodash@4.17.14/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.
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.
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);
});
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.