insertar - llamar javascript desde html
Hacer Javascript hacer una lista de comprensión (10)
Entonces, las comprensiones de la lista de Python realmente hacen dos cosas a la vez: mapeo y filtrado. Por ejemplo:
list_of_names = [x.name for x in list_of_object if x.enabled]
Si solo quiere la parte de mapeo, como muestra su ejemplo, puede usar la función de mapa de jQuery. Si también necesita filtrado puede usar la función "grep" de jQuery.
¿Cuál es la forma más limpia de hacer que Javascript haga algo como la comprensión de la lista de Python?
En Python, si tengo una lista de objetos cuyo nombre quiero ''sacar'', haría esto ...
list_of_names = [x.name for x in list_of_objects]
En javascript, realmente no veo una forma más ''hermosa'' de hacer eso que no sea simplemente usar una construcción for loop.
FYI: estoy usando jQuery; tal vez tiene alguna característica ingeniosa que hace esto posible?
Más específicamente, digamos que utilizo un selector de jQuery como $(''input'')
para obtener todos input
elementos de input
, ¿cómo podría crear limpiamente una matriz de todos los atributos de name
para cada uno de estos elementos de input
, es decir, todos los $(''input'').attr(''name'')
cadenas en una matriz?
Este es un ejemplo de un lugar donde CoffeeScript realmente brilla
pows = [x**2 for x in foo_arr]
list_of_names = [x.name for x in list_of_objects]
El Javascript equivalente sería:
var list_of_names, pows, x;
pows = [
(function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = foo_arr.length; _i < _len; _i++) {
x = foo_arr[_i];
_results.push(Math.pow(x, 2));
}
return _results;
})()
];
list_of_names = [
(function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = list_of_objects.length; _i < _len; _i++) {
x = list_of_objects[_i];
_results.push(x.name);
}
return _results;
})()
];
Hay un enfoque de una línea, implica el uso de una función de cierre anidado en el constructor de la lista. Y una función que lleva mucho tiempo para generar la secuencia. Se define a continuación:
var __ = generate = function(initial, max, list, comparision) {
if (comparision(initial))
list.push(initial);
return (initial += 1) == max + 1 ? list : __(initial, max, list, comparision);
};
[(function(l){ return l; })(__(0, 30, [], function(x) { return x > 10; }))];
// returns Array[20]
var val = 16;
[(function(l){ return l; })(__(0, 30, [], function(x) { return x % val == 4; }))];
// returns Array[2]
Esta es una implementación basada en rango como el rango de Python (mínimo, máximo) Además, la comprensión de la lista sigue esta forma:
[{closure function}({generator function})];
algunas pruebas:
var alist = [(function(l){ return l; })(__(0, 30, [], function(x) { return x > 10; }))];
var alist2 = [(function(l){ return l; })(__(0, 1000, [], function(x) { return x > 10; }))];
// returns Array[990]
var alist3 = [(function(l){ return l; })(__(40, 1000, [], function(x) { return x > 10; }))];
var threshold = 30*2;
var alist3 = [(function(l){ return l; })(__(0, 65, [], function(x) { return x > threshold; }))];
// returns Array[5]
Si bien esta solución no es la más limpia, hace el trabajo. Y en producción probablemente recomendaría no hacerlo.
Por último, uno puede elegir no usar recursividad para mi método "generar", ya que haría el trabajo más rápido. O mejor aún, use una función incorporada de las muchas bibliotecas populares de Javascript. Aquí hay una implementación sobrecargada que también acomodaría las propiedades de los objetos
// A list generator overload implementation for
// objects and ranges based on the arity of the function.
// For example [(function(l){ return l; })(__(0, 30, [], function(x) { return x > 10; }))]
// will use the first implementation, while
// [(function(l){ return l; })(__(objects, ''name'', [], function(x, y) { var x = x || {}; return x[y] }))];
// will use the second.
var __ = generator = function(options) {
return arguments.length == 4 ?
// A range based implemention, modeled after pythons range(0, 100)
(function (initial, max, list, comparision) {
var initial = arguments[0], max = arguments[1], list = arguments[2], comparision = arguments[3];
if (comparision(initial))
list.push(initial);
return (initial += 1) == max + 1 ? list : __(initial, max, list, comparision);
})(arguments[0], arguments[1], arguments[2], arguments[3]):
// An object based based implementation.
(function (object, key, list, check, acc) {
var object = arguments[0], key = arguments[1], list = arguments[2], check = arguments[3], acc = arguments[4];
acc = acc || 0;
if (check(object[acc], key))
list.push(object[acc][key]);
return (acc += 1) == list.length + 1 ? list : __(object, key, list, check, acc);
})(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
};
Uso:
var threshold = 10;
[(function(l){ return l; })(__(0, 65, [], function(x) { return x > threshold; }))];
// returns Array[5] -> 60, 61, 62, 63, 64, 65
var objects = [{''name'': ''joe''}, {''name'': ''jack''}];
[(function(l){ return l; })(__(objects, ''name'', [], function(x, y) { var x = x || {}; return x[y] }))];
// returns Array[1] -> [''Joe'', ''Jack'']
[(function(l){ return l; })(__(0, 300, [], function(x) { return x > 10; }))];
¡La sintaxis es asquerosa!
La mejor de las suertes.
Las comprensiones de matriz son parte del borrador de ECMAScript 6. Actualmente (enero de 2014) solo el JavaScript de Mozilla / Firefox los implementa.
var numbers = [1,2,3,4];
var squares = [i*i for (i of numbers)]; // => [1,4,9,16]
var somesquares = [i*i for (i of numbers) if (i > 2)]; // => [9,16]
Aunque ECMAScript 6 cambió recientemente a sintaxis de izquierda a derecha, similar a C # y F #:
var squares = [for (i of numbers) i*i]; // => [1,4,9,16]
http://kangax.github.io/es5-compat-table/es6/#Array_comprehensions
Los interesados en el "hermoso" Javascript probablemente deberían consultar CoffeeScript , un lenguaje que se compila en Javascript. Esencialmente existe porque a Javascript le faltan cosas como la comprensión de la lista.
En particular, la comprensión de la lista de Coffeescript es aún más flexible que la de Python. Vea la lista de documentos de comprensión aquí .
Por ejemplo, este código daría como resultado una matriz de atributos de name
de elementos de input
.
[$(inp).attr(''name'') for inp in $(''input'')]
Sin embargo, un posible inconveniente es que el Javascript resultante es detallado (y en mi humilde opinión):
var inp;
[
(function() {
var _i, _len, _ref, _results;
_ref = $(''input'');
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
inp = _ref[_i];
_results.push($(inp).attr(''name''));
}
return _results;
})()
];
Sí, también echo de menos las comprensiones de la lista.
Aquí hay una respuesta que es un poco menos detallada que la respuesta de @ gonchuki y la convierte en una matriz real, en lugar de un tipo de objeto.
var list_of_names = $(''input'').map(function() {
return $(this).attr(''name'');
}).toArray();
Un caso de uso de esto es tomar todas las casillas marcadas y unirlas al hash de la URL, así:
window.location.hash = $(''input:checked'').map(function() {
return $(this).attr(''id'');
}).toArray().join('','');
Una forma reutilizable de hacer esto es crear un pequeño complemento jQuery como este:
jQuery.fn.getArrayOfNames = function() {
var arr = [];
this.each( function() { arr.push(this.name || ''''); } );
return arr;
};
Entonces podrías usarlo así:
var list_of_names = $(''input'').getArrayOfNames();
No es una lista de comprensión, pero eso no existe en javascript. Todo lo que puedes hacer es usar javascript y jquery para lo que es bueno.
Una lista de comprensión tiene algunas partes.
- Seleccionar un conjunto de algo
- De un conjunto de Algo
- Filtrado por algo
En JavaScript, a partir de ES5 (así que creo que es compatible con IE9 +, Chrome y FF), puede usar las funciones de map
y filter
en una matriz.
Puedes hacer esto con un mapa y un filtro:
var list = [1,2,3,4,5].filter(function(x){ return x < 4; })
.map(function(x) { return ''foo '' + x; });
console.log(list); //["foo 1", "foo 2", "foo 3"]
Eso es casi tan bueno como lo que va a obtener sin configurar métodos adicionales o utilizar otro marco.
En cuanto a la pregunta específica ...
Con jQuery:
$(''input'').map(function(i, x) { return x.name; });
Sin jQuery:
var inputs = [].slice.call(document.getElementsByTagName(''input''), 0),
names = inputs.map(function(x) { return x.name; });
[].slice.call()
es solo para convertir NodeList
en una Array
.
Utilizando la función jQuery .each()
, puede recorrer cada elemento, obtener el índice del elemento actual y usar ese índice, puede agregar el atributo de nombre a la lista list_of_names
...
var list_of_names = new Array();
$(''input'').each(function(index){
list_of_names[index] = $(this).attr(''name'');
});
Si bien este es esencialmente un método de bucle, que usted especificó que no quería, es una implementación increíblemente clara de bucle y le permite ejecutar el bucle en selectores específicos.
Espero que ayude :)
caso genérico utilizando Array.map , requiere javascript 1.6 (es decir, funciona en todos los navegadores pero IE <9) o con un marco de aumento de objetos como MooTools funciona en cada navegador:
var list_of_names = document.getElementsByTagName(''input'').map(
function(element) { return element.getAttribute(''name''); }
);
El ejemplo específico de jQuery, funciona en todos los navegadores:
var list_of_names = jQuery.map(jQuery(''input''), function(element) { return jQuery(element).attr(''name''); });
las otras respuestas que usan .each
son incorrectas; no el código en sí, pero las implementaciones son subóptimas.
Editar: también hay una comprensión de Arrays introducida en Javascript 1.7, pero esto es puramente dependiente de la sintaxis y no puede ser emulado en los navegadores que carecen de forma nativa. Esto es lo más cercano que puede obtener en Javascript al fragmento de Python que publicó.