简体   繁体   中英

JavaScript Equivalent of C# ToDictionary

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

NpmRunKit 上的 ToDictionary 规范输出

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM