让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代码片段。

列表理解有几个部分。

  1. select一组东西
  2. 从一组东西
  3. 过滤的东西

在JavaScript中,从ES5开始(所以我认为在IE9 +,Chrome和FF中都支持),你可以在一个数组上使用mapfilter函数。

你可以用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器上运行循环。

希望帮助:)