【问题标题】:Javascript Equivalent to C# LINQ GroupByJavascript 等效于 C# LINQ GroupBy
【发布时间】:2018-01-02 05:48:53
【问题描述】:

这是对以下问题的后续问题:

Javascript Equivalent to C# LINQ Select

我们正在使用 Angular 2 + TypeScript:

我有一个对象数组。数组中的每个对象都包含一个名为“StudentType”的属性。

我需要运行 C# LINQ 样式查询,以提取数组中的 StudentType 列表以及具有该特定类型的数组成员的数量。

虽然我可以做一个老派循环来做这件事,但我想知道是否有更好的方法来做这件事,就像 C# LINQ GroupBy 提供的那样。

由于我们使用的是 Angular 2,项目负责人不允许使用 JQuery。

【问题讨论】:

标签: javascript angular typescript


【解决方案1】:

实际上,LINQ-Group-By 返回类似于字典的内容,其中 group-item 作为 key,element-array 作为 list 中的 object。

这可以在 JavaScript 中轻松完成。
这是 TypeScript 源代码:

// https://stackoverflow.com/questions/20310369/declare-a-delegate-type-in-typescript
// type Predicate<T, TKey> = (item: T) => TKey;

interface Predicate<T, TKey> 
{
    (item: T): TKey;
}


function LinqGroupBy<TSource, TKey>(source: TSource[], keySelector: Predicate<TSource, TKey>)
    : { [key: string]: TSource[] }
{
    if (source == null)
        throw new Error("ArgumentNullException: Source");
    if (keySelector == null)
        throw new Error("ArgumentNullException: keySelector");

    let dict: { [key: string]: TSource[]} = {};

    for (let i = 0; i < source.length; ++i)
    {
        let key: string = String(keySelector(source[i]));

        if (!dict.hasOwnProperty(key))
        {
            dict[key] = [];
        }

        dict[key].push(source[i]);
    }

    return dict;
}

转译为:

function LinqGroupBy(source, keySelector) 
{
    if (source == null)
        throw new Error("ArgumentNullException: Source");
    if (keySelector == null)
        throw new Error("ArgumentNullException: keySelector");
    var dict = {};
    for (var i = 0; i < source.length; ++i) 
    {
        var key = String(keySelector(source[i]));
        if (!dict.hasOwnProperty(key)) 
        {
            dict[key] = [];
        }
        dict[key].push(source[i]);
    }
    return dict;
}

由于 JavaScript 中对象的性质,这有点不稳定,但它在大多数情况下都有效。

【讨论】:

    【解决方案2】:

    给你(两个选项):

    function GroupBy1(arr, key) {
        var group = {};
    
        arr.forEach(val => group[val[key]] = (group[val[key]] || 0) + 1);
    
        return group;
    }
    
    function GroupBy2(arr, key) {
        return arr.reduce((group, val) => {
            group[val[key]] = (group[val[key]] || 0) + 1;
    
            return count;
        }, {});
    }
    
    var arr = [
        { studentType: 'freshman' },
        { studentType: 'senior' },
        { studentType: 'freshman' },
        { studentType: 'junior' },
        { studentType: 'sophmore' },
        { studentType: 'freshman' },
        { studentType: 'freshman' },
        { studentType: 'junior' },
        { studentType: 'senior' },
        { studentType: 'senior' },
        { studentType: 'freshman' },
        { studentType: 'sophmore' },
        { studentType: 'freshman' },
        { studentType: 'sophmore' },
        { studentType: 'senior' }
    ];
    
    console.log(GroupBy1(arr, 'studentType'));
    console.log(GroupBy2(arr, 'studentType'));
    

    【讨论】:

      【解决方案3】:

      您可以尝试manipula 包中的groupBy,它实现了所有 C# LINQ 方法并保留了其语法。

      【讨论】:

        【解决方案4】:

        我在 TypeScript 中创建了一个 groupBy 函数。

        export interface Group {
          key: any;
          items: any[];
        }
        
        export interface GroupBy {
          keys: string[];
          thenby?: GroupBy;
        }
        
        export const groupBy = (array: any[], grouping: GroupBy): Group[] => {
          const keys = grouping.keys;
          const groups = array.reduce((groups, item) => {
            const group = groups.find(g => keys.every(key => item[key] === g.key[key]));
            const data = Object.getOwnPropertyNames(item)
              .filter(prop => !keys.find(key => key === prop))
              .reduce((o, key) => ({ ...o, [key]: item[key] }), {});
            return group
              ? groups.map(g => (g === group ? { ...g, items: [...g.items, data] } : g))
              : [
                  ...groups,
                  {
                    key: keys.reduce((o, key) => ({ ...o, [key]: item[key] }), {}),
                    items: [data]
                  }
                ];
          }, []);
          return grouping.thenby ? groups.map(g => ({ ...g, items: groupBy(g.items, grouping.thenby) })) : groups;
        };
        

        我在 StackBlitz 上做了一个演示

        https://stackblitz.com/edit/typescript-ezydzv

        【讨论】:

        • 已编辑以包含该功能。
        【解决方案5】:

        我刚刚编写了一些 studentType 值进行测试,但您可以使用 Array.prototype.reduce() 迭代输入数组的每个元素并添加或操作累加器对象的属性。

        let arr = [{
            studentType: 'freshman'
          },
          {
            studentType: 'senior'
          },
          {
            studentType: 'sophomore'
          },
          {
            studentType: 'freshman'
          },
          {
            studentType: 'junior'
          },
          {
            studentType: 'senior'
          },
          {
            studentType: 'sophomore'
          },
          {
            studentType: 'freshman'
          },
          {
            studentType: 'sophomore'
          },
          {
            studentType: 'freshman'
          }
        ];
        
        let result = arr.reduce((prev, curr) => {
          isNaN(prev[curr.studentType]) ? prev[curr.studentType] = 1 : prev[curr.studentType]++;
          return prev;
        }, {});
        
        console.log(result);

        【讨论】:

        • 如果在这种情况下您将 prev 参数重命名为 accumulatoraccresultcount 或类似名称,可能会对读者有所帮助。
        猜你喜欢
        • 2013-09-27
        • 2011-08-22
        • 1970-01-01
        • 1970-01-01
        • 2013-12-15
        • 2016-11-14
        • 1970-01-01
        • 1970-01-01
        • 2022-10-23
        相关资源
        最近更新 更多