Сделать Javascript do List понимание


каков самый чистый способ заставить Javascript сделать что-то как понимание списка Python?

в Python если у меня есть список объектов, чье имя я хочу "вытащить", я бы это сделал...

list_of_names = [x.name for x in list_of_objects]

в javascript я действительно не вижу более "красивого" способа сделать это, кроме как просто использовать конструкцию цикла for.

FYI: я использую jQuery; может быть, у него есть какая-то отличная функция, которая делает это возможным?

больше в частности, скажем, я использую селектор jQuery, например $('input') для всех input элементы, как бы я самых чистоплотных создать массив всех name атрибуты для каждого из этих input элементы, т. е. все $('input').attr('name') строки в массиве?

10 56

10 ответов:

общий случай с помощью массив.карта, требуется javascript 1.6 (что означает, работает на каждом браузере, но IE или С объектом дополняя рамки, как 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 ошибаются; не сам код, но реализации являются неоптимальными.

Edit: там же выделения массива введено в Javascript 1.7, но это чисто зависит от синтаксиса и не может быть эмулировано в браузерах, которые не имеют его изначально. Это самое близкое, что вы можете получить в Javascript к фрагменту Python, который вы опубликовали.

понимание списка имеет несколько частей к нему.

  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() это просто конвертировать NodeList до Array.

те, кто интересуется "красивым" 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;
  })()
];

Итак, понимание списка python на самом деле делает две вещи сразу: отображение и фильтрацию. Например:

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

Если вам просто нужна часть отображения, как показывает ваш пример, вы можете использовать функцию карты jQuery. Если вам также нужна фильтрация, вы можете использовать функцию "grep" jQuery.

многоразовый способ сделать это-создать крошечный плагин 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 для того, что это хорошо.

понимание массива является частью проекта ECMAScript 6. В настоящее время (январь 2014) их реализует только JavaScript Mozilla/Firefox.

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

Да-я тоже скучаю по списку понимания.

вот ответ, который немного менее подробный, чем ответ @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(',');

существует однострочный подход, он включает в себя использование вложенной функции закрытия в конструктор списка. И функция, которая идет долго с ним, чтобы генерировать последовательность. Его определяют ниже:

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

некоторые тесты:

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]

хотя это решение не является самым чистым, оно выполняет свою работу. И в производстве я бы наверное, не советую этого делать.

наконец, можно выбрать не использовать рекурсию для моего метода "generate", поскольку это сделает работу быстрее. Или даже лучше использовать встроенную функцию из многих популярных библиотек 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; }))];

синтаксис отстой я знаю!

удачи.

Это пример места, где 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');
});

хотя это по существу метод цикла, который вы указали, что вы не хотите, это невероятно аккуратная реализация цикла и позволяет запускать цикл на определенных селекторах.

надеюсь, что это поможет:)