web-dev-qa-db-ja.com

Javascriptでリストを理解させる

Javascriptにlikeを実行させる最もクリーンな方法は何ですか?

In Python名前を「引き出し」たいオブジェクトのリストがある場合、これを行います...

_list_of_names = [x.name for x in list_of_objects]
_

Javascriptでは、forループ構造を使用する以外に、それを行う「美しい」方法は実際にはありません。

参考までに、jQueryを使用しています。多分それはこれを可能にするいくつかの気の利いた機能を持っていますか?

具体的には、$('input')などのjQueryセレクターを使用してすべてのinput要素を取得するとします。どのようにすればmost cleanlyこれらの各name要素のすべてのinput属性-つまり、配列内のすべての$('input').attr('name')文字列?

57
Chris W.

Array.map を使用する一般的な場合、javascript 1.6が必要です(つまり、IE <9)orMooToolsのようなオブジェクト拡張フレームワークでは、すべてのブラウザーで動作します。

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に導入されましたが、これは純粋に構文に依存しており、ブラウザではエミュレートできませんネイティブに欠けています。これは、あなたが投稿したPythonスニペットにJavascriptで最も近いものです。

50
gonchuki

リストの理解には、いくつかの部分があります。

  1. 何かのセットを選択する
  2. 何かのセットから
  3. 何かでフィルタリング

JavaScriptでは、ES5の時点で(つまりIE9 +でサポートされていると思うChromeおよびFF)、配列で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()は、単にNodeListArrayに変換するためのものです。

11
Ben Lesh

「美しい」Javascriptに興味がある人は、おそらく CoffeeScript (Javascriptにコンパイルされる言語)をチェックしてください。 Javascriptにはリストの理解のようなものが欠けているため、本質的に存在します。

特に、Coffeescriptのリストの理解は、Pythonのリストよりもさらに柔軟です。 ここに内包ドキュメントをリストしてください を参照してください。

たとえば、このコードはname要素のinput属性の配列になります。

[$(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;
  })()
];
6
Chris W.

そのため、Pythonのリスト内包表記は実際には、マッピングとフィルタリングという2つのことを同時に行います。例えば:

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

マッピング部分だけが必要な場合は、例に示すように、jQueryのマップ機能を使用できます。フィルタリングも必要な場合は、jQueryの「grep」機能を使用できます。

4
jpsimons

配列内包表記は、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

2
Brian Burns

これを行う再利用可能な方法は、次のような小さな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を使用することだけです。

2
typeof

ええ—私もリストの理解が恋しいです。

これは、@ 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(',');
1
jedmao

これは、 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;
  })()
];
1
Evan

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のrange(min、max)のような範囲ベースの実装です。さらに、リストの内包表記は次の形式に従います。

[{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; }))];

私が知っている構文はひどいです!

幸運を祈ります。

1
user2567070

JQuery .each()関数を使用して、各要素をループし、現在の要素のインデックスを取得し、そのインデックスを使用して、name属性をlist_of_names配列に追加できます...

var list_of_names = new Array();

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

これは基本的には望まないループ方法ですが、ループの信じられないほどきれいな実装であり、特定のセレクターでループを実行できます。

それが役立つことを願っています:)

0
Damien-Wright