Javascript equivalente a C # LINQ Select

137

Siguiendo esta pregunta aquí:

El uso del enlace marcado en el knockout con una lista de casillas de verificación marca todas las casillas de verificación

He creado algunas casillas de verificación usando knockout que permiten la selección de una matriz. violín de trabajo tomado de la publicación anterior:

http://jsfiddle.net/NsCXJ/

¿Hay una manera simple de crear una matriz de solo las identificaciones de la fruta?

Estoy más en casa con C #, donde haría algo similar a selectedFruits.select(fruit=>fruit.id);

¿Hay algún método / función preparada para hacer algo similar con javascript / jquery? ¿O la opción más simple sería recorrer la lista y crear una segunda matriz? Tengo la intención de publicar la matriz de nuevo en el servidor en JSON, así que estoy tratando de minimizar los datos enviados.

Chris Nevill
fuente

Respuestas:

227

Sí, Array.map () o $ .map () hace lo mismo.

//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/

Dado que array.map no es compatible con navegadores antiguos, le sugiero que siga con el método jQuery.

Si prefiere el otro por alguna razón, siempre puede agregar un polyfill para el soporte antiguo del navegador.

Siempre puede agregar métodos personalizados al prototipo de matriz también:

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

Una versión extendida que usa el constructor de funciones si pasa una cadena. Algo para jugar, tal vez:

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/

Actualizar:

Como esta se ha convertido en una respuesta tan popular, agrego mi where()+ similar firstOrDefault(). Estos también podrían usarse con el enfoque del constructor de funciones basado en cadenas (que es el más rápido), pero aquí hay otro enfoque que usa un objeto literal como filtro:

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

Uso:

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

Aquí hay una prueba de jsperf para comparar el constructor de funciones frente a la velocidad literal del objeto. Si decide usar el primero, recuerde citar las cadenas correctamente.

Mi preferencia personal es usar las soluciones basadas en literales de objeto al filtrar 1-2 propiedades, y pasar una función de devolución de llamada para un filtrado más complejo.

Terminaré esto con 2 consejos generales al agregar métodos a los prototipos de objetos nativos:

  1. Verifique la existencia de métodos existentes antes de sobrescribir, por ejemplo:

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

  2. Si no necesita admitir IE8 y versiones posteriores, defina los métodos con Object.defineProperty para que no se puedan enumerar . Si alguien usó for..inen una matriz (lo cual es incorrecto en primer lugar), también iterará propiedades enumerables. Solo un aviso.

Johan
fuente
1
@ChrisNevill También agregué una versión de cadena en caso de que estés interesado
Johan
@MUlferts Buena captura, actualizada :). Hoy en día sugeriría usar lodash para este tipo de tareas. Exponen la misma interfaz que el código anterior
Johan
Para apoyar los observables eliminados:return typeof item[property] === 'function' ? item[property]() === filter[property] : item[property] === filter[property];
Linus Caldwell
@LinusCaldwell Ha pasado mucho tiempo desde que usé knockout, pero ¿qué pasa con algo así return ko.unwrap(item[property]) === filter[property]?
Johan
Bueno, mencioné knockout, pero por supuesto esto cubriría todas las propiedades que son funciones sin los parámetros requeridos. Además, ¿por qué uno rompería el estilo genérico de su hermoso código?
Linus Caldwell
33

Sé que es una respuesta tardía, ¡pero me fue útil! Solo para completar, usando la $.grepfunción puedes emular el linq where().

Linq:

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; });
Stefano Altieri
fuente
eso es lo que quiero ... pero lo que es más bueno entre su respuesta y Enumerable. De (selectedFruits). Seleccione (function (fruit) {return fruit.id;});
Bharat
15

Dado que está utilizando la función de eliminación, debe considerar usar la función de utilidad de eliminación arrayMap()y sus otras funciones de utilidad de matriz.

Aquí hay una lista de funciones de utilidad de matriz y sus métodos LINQ equivalentes:

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)

Entonces, lo que podría hacer en su ejemplo es esto:

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

Si desea una interfaz similar a LINQ en javascript, puede usar una biblioteca como linq.js que ofrece una buena interfaz para muchos de los métodos LINQ.

var mapped = Enumerable.From(selectedFruits)
    .Select("$.id") // 1 of 3 different ways to specify a selector function
    .ToArray();
Jeff Mercado
fuente
14

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

también en: https://jsfiddle.net/52dpucey/

Julio.Tech
fuente
Muy apreciado. Me estoy metiendo en ES6, ¡así que esto podría ser útil!
Chris Nevill
10

También puedes probar linq.js

En linq.jssu

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

estarán

Enumerable.From(selectedFruits).Select(function (fruit) { return fruit.id;  });
Anik Islam Abhi
fuente
4

He creado una biblioteca de Linq para TypeScript en TsLinq.codeplex.com que también puede usar para JavaScript simple. Esa biblioteca es 2-3 veces más rápida que Linq.js y contiene pruebas unitarias para todos los métodos de Linq. Tal vez podrías revisar eso.

Michael Baarz
fuente
2

Eche un vistazo a underscore.js que proporciona muchas funciones similares a linq. En el ejemplo que le daría, usaría la función de mapa.

Conejito Gruff
fuente
1
Si alguien quiere saber cómo se comparan, hice una publicación de blog que explica la diferencia entre las más de 15 funciones más populares de LINQ / underscore.js: vladopandzic.com/javascript/comparing-underscore-js-with-linq
Vlado Pandžić
0

Dinqyjs tiene una sintaxis tipo linq y proporciona polyfills para funciones como map e indexOf, y ha sido diseñado específicamente para trabajar con matrices en Javascript.

garryp
fuente
0

Eche un vistazo a fluido , es compatible con casi todo lo que hace LINQ y se basa en iterables, por lo que funciona con mapas, funciones de generador, matrices, todo iterable.

kataik
fuente
-1

Estoy respondiendo el título de la pregunta en lugar de la pregunta original que era más específica.

Con las nuevas características de Javascript como iteradores y funciones y objetos del generador, es posible algo como LINQ for Javascript. Tenga en cuenta que linq.js, por ejemplo, utiliza un enfoque completamente diferente, utilizando expresiones regulares, probablemente para superar la falta de soporte en el idioma en ese momento.

Dicho esto, he escrito una biblioteca LINQ para Javascript y puede encontrarla en https://github.com/Siderite/LInQer . Comentarios y discusión en https://siderite.dev/blog/linq-in-javascript-linqer .

De las respuestas anteriores, solo Manipula parece ser lo que uno esperaría de un puerto LINQ en Javascript.

Siderite Zackwehdex
fuente