最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

dictionary - JavaScript Equivalent of C# ToDictionary - Stack Overflow

programmeradmin0浏览0评论

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
  • 1 You can do it with .reduce(). – Pointy Commented Jul 17, 2019 at 20:38
  • 2 Note the first structure allows more than one entry for the same name, while the one you want don't. – Shidersz Commented Jul 17, 2019 at 20:42
  • linq.js offers a toDictionary method. – Kenneth K. Commented Jul 17, 2019 at 20:44
  • You can write it. Think: extension methods; just a warning, in JS they are global, so be careful about collisions with other libraries/ES6/7/8/9... But ultimately, a good old for(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
  • 1 you should be using Map - it was specifically designed for creating a lookup table like this – Mulan Commented Jul 18, 2019 at 14:47
 |  Show 1 more comment

6 Answers 6

Reset to default 6

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

发布评论

评论列表(0)

  1. 暂无评论