让JavaScript做列表理解
什么是最简洁的方法来使JavaScript做类似 Python的列表理解?
在Python中,如果我有一个名称是我想“拉出”的对象列表,我会这样做…
list_of_names = [x.name for x in list_of_objects]
在JavaScript中,我并没有真正看到除了使用for循环构造之外的更“美丽”的方式。
仅供参考:我正在使用jQuery; 也许它有一些漂亮的function,使这成为可能?
更具体地说,假设我使用像$('input')
这样的jQueryselect器来获取所有input
元素,那么我将如何最干净地为每个input
元素创build一个所有name
属性的数组 – 即所有的$('input').attr('name')
数组中的string?
使用Array.map的一般情况下,需要JavaScript 1.6(这意味着,每个浏览器,但IE浏览器<9) 或像MooTools对象扩展框架适用于每个浏览器:
var list_of_names = document.getElementsByTagName('input').map( function(element) { return element.getAttribute('name'); } );
jQuery的具体例子,适用于每一个浏览器:
var list_of_names = jQuery.map(jQuery('input'), function(element) { return jQuery(element).attr('name'); });
其他答案使用.each
是错误的; 不是代码本身,但实现是次优的。
编辑:也有在JavaScript 1.7中引入的数组理解 ,但这完全依赖于语法,不能在浏览器上本地缺乏模拟。 这是您可以在JavaScript中获得最接近你发布的Python代码片段。
列表理解有几个部分。
- select一组东西
- 从一组东西
- 过滤的东西
在JavaScript中,从ES5开始(所以我认为在IE9 +,Chrome和FF中都支持),你可以在一个数组上使用map
和filter
函数。
你可以用map和filter来做到这一点:
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"]
没有设置额外的方法或使用其他框架,这就好了。
至于具体的问题…
使用jQuery:
$('input').map(function(i, x) { return x.name; });
没有jQuery:
var inputs = [].slice.call(document.getElementsByTagName('input'), 0), names = inputs.map(function(x) { return x.name; });
[].slice.call()
只是将NodeList
转换为一个Array
。
那些对“美丽的”Javascript感兴趣的人可能应该检查一下CoffeeScript ,一种编译成Javascript的语言。 它基本上存在,因为Javascript缺less列表理解的东西。
特别是,Coffeescript的列表理解比Python更灵活。 在这里查看列表理解文档 。
例如,这个代码将导致input
元素的name
属性数组。
[$(inp).attr('name') for inp in $('input')]
然而,一个潜在的缺点是由此产生的Javascript是冗长的(和恕我直言混淆):
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; })() ];
所以,Python的列表parsing实际上是同时做两件事:映射和过滤。 例如:
list_of_names = [x.name for x in list_of_object if x.enabled]
如果你只是想要映射部分,如你的例子所示,你可以使用jQuery的地图function。 如果你还需要过滤,你可以使用jQuery的“grep”function。
这样做的一个可重用的方法是创build一个像这样的小jQuery插件:
jQuery.fn.getArrayOfNames = function() { var arr = []; this.each( function() { arr.push(this.name || ''); } ); return arr; };
那么你可以像这样使用它:
var list_of_names = $('input').getArrayOfNames();
这不是列表理解,但在JavaScript中不存在。 你所能做的就是使用javascript和jquery来实现它的优点。
是的,我也想念列表parsing。
这里的答案比@ gonchuki的答案略less,并把它转换成实际的数组,而不是一个对象types。
var list_of_names = $('input').map(function() { return $(this).attr('name'); }).toArray();
一个用例是将所有选中的checkbox都join到URL的哈希中,如下所示:
window.location.hash = $('input:checked').map(function() { return $(this).attr('id'); }).toArray().join(',');
有一种方法,它涉及在列表的构造函数中使用嵌套的闭包函数。 还有一个函数,用来生成序列。 其定义如下:
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]
这是一个基于范围的实现,如Python的范围(min,max)另外,列表理解如下:
[{closure function}({generator function})];
一些testing:
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]
虽然这个解决scheme并不是最干净的,但它完成了工作。 而在生产中,我可能会提出反对意见。
最后,我们可以select不使用recursion来生成我的“生成”方法,因为它可以更快地完成工作。 或者甚至更好地使用来自许多stream行的Javascript库的内置函数。 这是一个重载的实现,也将适应对象的属性
// 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]); };
用法:
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; }))];
语法糟透了,我知道!
祝你好运。
数组理解是ECMAScript 6草案的一部分。 目前(2014年1月)只有Mozilla / Firefox的JavaScript实现它们。
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]
虽然ECMAScript 6最近转换为从左到右的语法,类似于C#和F#:
var squares = [for (i of numbers) i*i]; // => [1,4,9,16]
http://kangax.github.io/es5-compat-table/es6/#Array_comprehensions
这是Coffeescript真正闪耀的一个例子
pows = [x**2 for x in foo_arr] list_of_names = [x.name for x in list_of_objects]
等效的Javascript将是:
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; })() ];
使用jQuery .each()
函数,可以遍历每个元素,获取当前元素的索引,并使用该索引,可以将name属性添加到list_of_names
数组中…
var list_of_names = new Array(); $('input').each(function(index){ list_of_names[index] = $(this).attr('name'); });
虽然这本质上是一个循环方法,您指定的循环方法是您不想要的,但它是一个令人难以置信的循环实现,并允许您在特定的select器上运行循环。
希望帮助:)