【问题标题】:Javascript Equivalent to C# LINQ Select等效于 C# LINQ Select 的 Javascript
【发布时间】:2013-09-27 00:32:35
【问题描述】:

在这里关注这个问题:

Using the checked binding in knockout with a list of checkboxes checks all the checkboxes

我使用敲除创建了一些复选框,允许从数组中进行选择。 从上面的帖子中提取的工作小提琴:

http://jsfiddle.net/NsCXJ/

有没有一种简单的方法来创建一个只包含水果 ID 的数组?

我更喜欢使用 C#,我会按照 selectedFruits.select(fruit=>fruit.id); 的方式做一些事情

是否有一些方法/现成的功能可以用 javascript/jquery 做类似的事情?或者最简单的选择是遍历列表并创建第二个数组? 我打算将数组以 JSON 格式发回服务器,因此我试图最小化发送的数据。

【问题讨论】:

    标签: javascript jquery knockout.js


    【解决方案1】:

    是的,Array.map()$.map() 做同样的事情。

    //array.map:
    var ids = this.fruits.map(function(v){
        return v.Id;
    });
    
    //jQuery.map:
    var ids2 = $.map(this.fruits, function (v){
        return v.Id;
    });
    
    console.log(ids, ids2);
    

    http://jsfiddle.net/NsCXJ/1/

    由于旧浏览器不支持array.map,我建议你坚持使用jQuery方法。

    如果您出于某种原因更喜欢另一个,您可以随时添加一个 polyfill 以支持旧浏览器。

    您也可以随时向数组原型添加自定义方法:

    Array.prototype.select = function(expr){
        var arr = this;
        //do custom stuff
        return arr.map(expr); //or $.map(expr);
    };
    
    var ids = this.fruits.select(function(v){
        return v.Id;
    });
    

    如果您传递字符串,则使用函数构造函数的扩展版本。也许可以玩的东西:

    Array.prototype.select = function(expr){
        var arr = this;
    
        switch(typeof expr){
    
            case 'function':
                return $.map(arr, expr);
                break;
    
            case 'string':
    
                try{
    
                    var func = new Function(expr.split('.')[0], 
                                           'return ' + expr + ';');
                    return $.map(arr, func);
    
                }catch(e){
    
                    return null;
                }
    
                break;
    
            default:
                throw new ReferenceError('expr not defined or not supported');
                break;
        }
    
    };
    
    console.log(fruits.select('x.Id'));
    

    http://jsfiddle.net/aL85j/

    更新:

    由于这已成为如此受欢迎的答案,因此我添加了类似的 where() + firstOrDefault()。这些也可以与基于字符串的函数构造方法一起使用(这是最快的),但这是另一种使用对象文字作为过滤器的方法:

    Array.prototype.where = function (filter) {
    
        var collection = this;
    
        switch(typeof filter) { 
    
            case 'function': 
                return $.grep(collection, filter); 
    
            case 'object':
                for(var property in filter) {
                  if(!filter.hasOwnProperty(property)) 
                      continue; // ignore inherited properties
    
                  collection = $.grep(collection, function (item) {
                      return item[property] === filter[property];
                  });
                }
                return collection.slice(0); // copy the array 
                                          // (in case of empty object filter)
    
            default: 
                throw new TypeError('func must be either a' +
                    'function or an object of properties and values to filter by'); 
        }
    };
    
    
    Array.prototype.firstOrDefault = function(func){
        return this.where(func)[0] || null;
    };
    

    用法:

    var persons = [{ name: 'foo', age: 1 }, { name: 'bar', age: 2 }];
    
    // returns an array with one element:
    var result1 = persons.where({ age: 1, name: 'foo' });
    
    // returns the first matching item in the array, or null if no match
    var result2 = persons.firstOrDefault({ age: 1, name: 'foo' }); 
    

    这是一个jsperf test,用于比较函数构造函数与对象字面量的速度。如果您决定使用前者,请记住正确引用字符串。

    我个人的偏好是在过滤1-2个属性时使用基于对象字面量的方案,并通过回调函数进行更复杂的过滤。

    在向原生对象原型添加方法时,我将用 2 个一般提示来结束本文:

    1. 在覆盖之前检查现有方法的出现,例如:

      if(!Array.prototype.where) { Array.prototype.where = ...

    2. 如果您不需要支持 IE8 及以下版本,请使用 Object.defineProperty 定义方法,使其不可枚举。如果有人在数组上使用了for..in(这首先是错误的) 他们也将迭代可枚举的属性。请注意。

    【讨论】:

    • @ChrisNevill 我还添加了一个字符串版本,以防你感兴趣
    • @MUlferts 很好,已更新:)。现在我建议使用 lodash 来完成这些类型的任务。它们暴露了与上面代码相​​同的接口
    • 支持淘汰观察:return typeof item[property] === 'function' ? item[property]() === filter[property] : item[property] === filter[property];
    • @LinusCaldwell 我已经很久没有使用淘汰赛了,但是像return ko.unwrap(item[property]) === filter[property]这样的东西呢?
    • 好吧,我提到了淘汰赛,但当然这将涵盖所有没有必需参数的函数的属性。此外,为什么要破坏你漂亮代码的通用风格?
    【解决方案2】:

    我知道这是一个迟到的答案,但它对我很有用!只是为了完成,使用 $.grep 函数,您可以模拟 linq where()

    林克:

    var maleNames = people
    .Where(p => p.Sex == "M")
    .Select(p => p.Name)
    

    Javascript:

    // replace where  with $.grep
    //         select with $.map
    var maleNames = $.grep(people, function (p) { return p.Sex == 'M'; })
                .map(function (p) { return p.Name; });
    

    【讨论】:

    • 这就是我想要的......但是在你的答案和 Enumerable.From(selectedFruits).Select(function (fruit) { return fruit.id; }); 之间更好的是什么。
    【解决方案3】:

    ES6 方式:

    let people = [{firstName:'Alice',lastName:'Cooper'},{firstName:'Bob',age:'Dylan'}];
    let names = Array.from(people, p => p.firstName);
    for (let name of names) {
      console.log(name);
    }
    

    也在:https://jsfiddle.net/52dpucey/

    【讨论】:

    • 非常感谢。我刚刚进入 ES6,所以这可能很方便!
    【解决方案4】:

    由于您使用的是knockout,您应该考虑使用knockout 实用函数arrayMap() 以及其他数组实用函数。

    以下是数组实用函数及其等效 LINQ 方法的列表:

    arrayFilter() -> Where()
    arrayFirst() -> First()
    arrayForEach() -> (no direct equivalent)
    arrayGetDistictValues() -> Distinct()
    arrayIndexOf() -> IndexOf()
    arrayMap() -> Select()
    arrayPushAll() -> (no direct equivalent)
    arrayRemoveItem() -> (no direct equivalent)
    compareArrays() -> (no direct equivalent)
    

    所以你可以在你的例子中做的是:

    var mapped = ko.utils.arrayMap(selectedFruits, function (fruit) {
        return fruit.id;
    });
    

    如果您想在 javascript 中使用类似 LINQ 的接口,您可以使用诸如 linq.js 之类的库,它为许多 LINQ 方法提供了一个很好的接口。

    var mapped = Enumerable.from(selectedFruits)
        .select("$.id") // shorthand for `x => x.id`
        .toArray();
    

    【讨论】:

      【解决方案5】:

      你也可以试试linq.js

      linq.js你的

      selectedFruits.select(fruit=>fruit.id);
      

      将是

      Enumerable.From(selectedFruits).Select(function (fruit) { return fruit.id;  });
      

      【讨论】:

        【解决方案6】:

        我在TsLinq.codeplex.com 下为 TypeScript 构建了一个 Linq 库,您也可以将其用于纯 javascript。该库比 Linq.js 快 2-3 倍,并且包含所有 Linq 方法的单元测试。也许你可以回顾一下。

        【讨论】:

          【解决方案7】:

          最相似的 C# Select 模拟将是 map 函数。 只需使用:

          var ids = selectedFruits.map(fruit => fruit.id);
          

          selectedFruits数组中选择所有id。

          它不需要任何外部依赖,只需要纯 JavaScript。你可以在这里找到map 文档:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

          【讨论】:

            【解决方案8】:

            看看underscore.js,它提供了许多类似 linq 的功能。在您提供的示例中,您将使用 map 函数。

            【讨论】:

            【解决方案9】:

            你可以试试manipula 包,它实现了所有的 C# LINQ 方法并保存了它的语法: https://github.com/litichevskiydv/manipula

            https://www.npmjs.com/package/manipula

            你的例子selectedFruits.select(fruit=>fruit.id); 将使用 manipula as 实现

            Manipula.from(selectedFruits).select(fruit=>fruit.id);
            

            【讨论】:

              【解决方案10】:

              Dinqyjs 具有类似 linq 的语法,并为 map 和 indexOf 等函数提供 polyfill,专为在 Javascript 中处理数组而设计。

              【讨论】:

                【解决方案11】:

                我正在回答问题的标题,而不是更具体的原始问题。

                有了迭代器和生成器函数和对象等 Javascript 的新特性,诸如 LINQ for Javascript 之类的东西成为可能。请注意,例如 linq.js 使用了完全不同的方法,使用正则表达式,这可能是为了克服当时语言缺乏支持的问题。

                话虽如此,我已经为 Javascript 编写了一个 LINQ 库,您可以在 https://github.com/Siderite/LInQer 找到它。评论和讨论https://siderite.dev/blog/linq-in-javascript-linqer

                从以前的答案来看,只有 Manipula 似乎是人们对 Javascript 中的 LINQ 端口所期望的。

                【讨论】:

                  【解决方案12】:

                  看看fluent,它几乎支持 LINQ 所做的一切并基于可迭代对象 - 因此它适用于映射、生成器函数、数组以及所有可迭代对象。

                  【讨论】:

                    【解决方案13】:

                    至于 2021 年,使用 ECMAScript (javascript) 的最新功能编写的 LINQ to object 的完整实现。

                    Github 存储库是: https://github.com/IlanAmoyal/WebPartyLinq

                    【讨论】:

                      猜你喜欢
                      • 2018-01-02
                      • 1970-01-01
                      • 2011-08-22
                      • 2010-09-11
                      • 2022-01-01
                      • 1970-01-01
                      • 2013-12-15
                      • 1970-01-01
                      • 2022-10-23
                      相关资源
                      最近更新 更多