【问题标题】:JavaScript Equivalent of C# ToDictionaryC# ToDictionary 的 JavaScript 等价物
【发布时间】:2019-11-26 18:05:43
【问题描述】:

我正在寻找一种方法来获取以下列表:

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"
    }
]

并在键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"
    }
}

这类似于 C# ToDictionary 方法。我知道我可以在for 循环或.each 调用中执行类似迭代directory 的操作,并在每次迭代时修改dictionary 的值。但是,我更愿意进行类似函数式编程的赋值,例如

dictionary = directory.toDictionary(p => p.name);

这样的方法是否存在,比如在 ES6 或 lodash 中?

【问题讨论】:

  • 您可以使用.reduce()
  • 请注意,第一个结构允许同名的多个条目,而您想要的则不允许。
  • linq.js 提供了一个toDictionary 方法。
  • 你可以写。思考:扩展方法;只是一个警告,在 JS 中它们是全局的,所以要小心与其他库/ES6/7/8/9 的冲突​​......但最终,一个好的旧 for(let i=0; i<array.length; ++i) 循环仍然是迭代数组的最快方法。
  • 你应该使用Map - 它是专门为创建这样的查找表而设计的

标签: javascript dictionary functional-programming


【解决方案1】:

您可以使用(即将推出的)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; }

经典方法

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

【讨论】:

    【解决方案2】:

    Javascript 的Map 非常类似于字典。它的实例很方便hasgetsetkeysvalues 方法。它也可以通过forEach 方法直接迭代。

    您可以使用“键值对”数组构造Map。 (在引号中,因为实际上我们以某种类似元组的方式使用数组)。

    要创建一个Map 用作字典,您可以:

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

    【讨论】:

      【解决方案3】:

      你可以reduce 数组。使用Object.assign() 将每个名称添加为属性

      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)

      【讨论】:

      • 这是一个很好的解决方案,因为它支持老年浏览器和Js标准。 Object.fromEntries 将需要导入 ES2017 库,例如,如果您使用 tsconfig 和 Angular。
      【解决方案4】:

      由于您也是通过lodash 解决方案提出的,您可以使用该库的.keyBy() 方法。

      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>

      但是,正如我在评论中所说,第一个结构允许同名的多个条目,而结果则不允许。因此,如果directory 数组包含多个具有相同名称的对象,您将只能在结果数据中获取它们中的最后一个。

      【讨论】:

        【解决方案5】:

        没有特定的内置功能,但使用.reduce() 很容易:

        let dict = directory.reduce((d, v) => (d[v.name] = v, d), {});
        

        正如对该问题的良好评论所指出的,您需要考虑的一件事是重复名称的含义。这是一个“坏数据”的情况吗?这些重复项是否应该有一组名称?此类问题与数据结构及其在您的特定应用程序中的用途有关。

        【讨论】:

        【解决方案6】:

        这是我添加 ToDictionary 实现的方法,它允许您定义一个键选择器,并允许每个键重复值,如果有很多值,这些值将保存到数组中。该方法是我在 Npmjs.com 上托管的库“simpletslinq”的 1.0.14 版的一部分

        请注意,我的代码是用 Typescript 编写的,因此您必须在其上运行 tsc typescript 编译器才能创建真正的 Javscript 等价物。在其核心中,我们使用 map 函数来创建字典。添加的打字稿的东西是添加类型检查..

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

        此代码的规范(测试)如下所示:

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

        【讨论】:

          猜你喜欢
          • 2018-12-02
          • 2022-06-20
          • 1970-01-01
          • 2016-08-30
          • 2015-09-27
          • 2017-06-19
          • 2016-11-27
          • 1970-01-01
          • 2010-10-05
          相关资源
          最近更新 更多