2011-02-11 27 views
54

什麼是最乾淨的方式來使Javascript做點什麼 Python的列表理解?使JavaScript做列表理解

在Python,如果我有他的名字的我想「拉出來」我會做這個對象的列表...

list_of_names = [x.name for x in list_of_objects] 

在javascript中我真的沒有看到更多的「美麗」的方式除了使用for循環結構外,

僅供參考:我正在使用jQuery;也許它有一些漂亮的功能,使這成爲可能?

更具體地說,說我使用jQuery選擇像$('input')讓所有input元素,我將如何最乾淨創建所有name屬性的數組,每個input元素 - 即所有$('input').attr('name')的數組中的字符串?

+1

你可以做得比Python好;-)好吧,這是一種奇怪的hackish,因爲語言的語法並不是全部,但...參見功能性JavaScript - http://osteele.com/sources/javascript /功能/(實際上,還有用Python生成器,但...額外魔法) – 2011-02-11 00:49:55

回答

47

一般情況下,需要JavaScript 1.6(這意味着,適用於所有的瀏覽器,但IE 9 <)有喜歡Moot對象增強框架適用於每一個瀏覽器:

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的其他答案是錯誤的;不是代碼本身,但實現是次優的。

編輯:還有還有Array comprehensions在Javascript 1.7推出,但這種純粹依賴於語法,不能在原生地缺乏它的瀏覽器效仿。這是您可以在Javascript中發佈到您發佈的Python代碼片段中最接近的東西。

+0

注意,我直接使用`jQuery的()`,而不是`$()`因爲我沒有把該代碼段封閉內:)感覺如果你在`(function($){})(jQuery)裏面運行`` – gonchuki 2011-02-11 01:03:54

+0

'我可以用`map`函數來理解你的實現嗎?它需要一個數組,然後一個函數...'jQuery('input')`數組怎麼樣? – 2011-02-11 01:05:56

+0

jQuery對象是一個類似數組的對象,因此它可以作爲實用程序模塊中包含的所有函數的第一個參數,它接受一個類似數組的對象作爲參數。如果你使用`jQuery('input')。map(...)`,那麼你應該用一個類似數組的對象(jQuery對象包裝一個數組)來代替香草javascript數組。 – gonchuki 2011-02-11 01:19:37

0

使用jQuery .each()功能,可以遍歷每個元素,得到當前元素的索引和使用該索引,可以將名字屬性添加到list_of_names陣列...

var list_of_names = new Array(); 

$('input').each(function(index){ 
    list_of_names[index] = $(this).attr('name'); 
}); 

這枚基本上是一個循環方法,你指定了你不想要的循環方法,它是一個令人難以置信的循環實現,並允許你在特定的選擇器上運行循環。

希望幫助:)

使用 Array.map
2

這樣做的一種可重複使用的方法是創建一個很小的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來實現它的優點。

4

因此,python的列表解析實際上同時做兩件事:映射和過濾。例如:

list_of_names = [x.name for x in list_of_object if x.enabled] 

如果您只是想要映射部分,如您的示例所示,您可以使用jQuery的映射功能。如果你還需要過濾,你可以使用jQuery的「grep」功能。

1

是的,我也懷念列表理解。

下面是一個答案,它比@ gonchuki的答案略少,並將其轉換爲實際數組,而不是對象類型。

var list_of_names = $('input').map(function() { 
    return $(this).attr('name'); 
}).toArray(); 

這個用例是把所有選中的複選框並把它們加入到URL的哈希值,像這樣:

window.location.hash = $('input:checked').map(function() { 
    return $(this).attr('id'); 
}).toArray().join(','); 
6

有興趣「美麗」的Javascript或許應該看看CoffeeScript,一種編譯爲Javascript的語言。它基本上存在是因爲Javascript缺少列表理解之類的東西。

特別是,Coffeescript的列表理解比Python更靈活。請參閱list comprehension docs here

例如,此代碼將導致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; 
    })() 
]; 
10

列表理解有幾個部分的。

  1. 選擇一組東西
  2. 從一組東西
  3. 由東西

過濾在JavaScript中,如ES5的(所以我認爲這是一個在IE9 +,Chrome和FF支持)您可以在陣列上使用mapfilter函數。

您可以通過地圖和過濾做到這一點:

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()只是到轉換210到Array

1

有一個行方法,它涉及在 構造函數列表的使用嵌套閉合功能。還有一個函數可以用來生成序列。 其定義如下:

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的範圍(最小值,最大值) 除了列表解析如下這種形式:

[{closure function}({generator function})]; 

一些測試:

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] 

雖然這種解決方案是不是能夠完成任務的乾淨。而在生產中,我可能會提出反對意見。

最後一個可以選擇不使用遞歸爲我的「生成」的方法,因爲它會做的工作更快。或者甚至更好地使用許多常用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; }))]; 

語法很爛,我知道!

祝你好運。

2

陣列內涵是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

1

這是一個地方的例子,其中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; 
    })() 
];