Как получить ключи массива в Javascript?


80

У меня есть массив, созданный с помощью этого кода:

var widthRange = new Array();
widthRange[46] = { min:0,  max:52 };
widthRange[66] = { min:52, max:70 };
widthRange[90] = { min:70, max:94 };

Я хочу получить каждое из значений 46, 66, 90 в цикле. Я пробовал, for (var key in widthRange)но это дает мне массу дополнительных свойств (я предполагаю, что они являются функциями объекта). Я не могу использовать обычный цикл for, поскольку значения не являются последовательными.


9
Похоже, у вас есть данные, которые, хотя и имеют числовые ключи, на самом деле не являются данными массива. Я бы посмотрел здесь на использование обычного объекта.
Квентин,

@Quentin Это называется разреженным массивом. С точки зрения производительности здесь лучше использовать массив вместо объекта. Кроме того, с точки зрения производительности, лучший ответ даже не в списке: Array.prototype.forEach. Вызов Object.keysмассива неэффективен, потому что браузеры не оптимизируют его. for(var key in array)плохо, потому что он проходит через прототип и преобразует каждый встреченный числовой ключ в цепочку (преобразование двойных чисел в base10 очень медленное). forEachоднако был разработан именно для итерации разреженных массивов и обеспечит превосходную производительность вашего кода по сравнению с другими решениями
Джек Гиффин,

Ответы:


96

Вам нужно вызвать hasOwnPropertyфункцию, чтобы проверить, действительно ли свойство определено в самом объекте (в отличие от его прототипа), например:

for (var key in widthRange) {
    if (key === 'length' || !widthRange.hasOwnProperty(key)) continue;
    var value = widthRange[key];
}

Обратите внимание, что вам нужна отдельная проверка для length.
Однако здесь вообще не следует использовать массив; вам следует использовать обычный предмет. Все объекты Javascript функционируют как ассоциативные массивы.

Например:

var widthRange = { };  //Or new Object()
widthRange[46] = { sel:46, min:0,  max:52 };
widthRange[66] = { sel:66, min:52, max:70 };
widthRange[90] = { sel:90, min:70, max:94 };

2
В самом деле? Вау, я узнаю здесь что-то каждый день, и так часто это от вас, SLaks :-) Я удивлен, что Array отслеживает такие явно установленные индексы. Может, мне не стоит.
Pointy

@SLaks: Спасибо, поменять его на объект кажется лучшим решением. Еще один вопрос: есть ли способ перебора в обратном порядке?
DisgruntledGoat

Нет; вам нужно будет сделать это самостоятельно.
SLaks

2
Зачем нужен отдельный чек length? Не помечено как [[DontEnum]]во всех браузерах?
Roatin Marth

@ Роатин: Не знаю. Береженого Бог бережет.
SLaks


18

Если вы выполняете какие-либо манипуляции с массивами / коллекциями или инспектируете их, я настоятельно рекомендую использовать Underscore.js . Он небольшой, хорошо протестирован и сэкономит вам дни / недели / годы головной боли с javascript. Вот его функции клавиш:

Ключи

Получить все имена свойств объекта.

_.keys({one : 1, two : 2, three : 3});
=> ["one", "two", "three"]

4

Скажите, что ваш массив выглядел arr = [ { a: 1, b: 2, c: 3 }, { a: 4, b: 5, c: 6 }, { a: 7, b: 8, c: 9 } ](или, возможно, другие ключи), вы могли бы сделать

arr.map((o) => {
    return Object.keys(o)
}).reduce((prev, curr) => {
    return prev.concat(curr)
}).filter((col, i, array) => {
    return array.indexOf(col) === i
});

["a", "b", "c"]


3
for (var i = 0; i < widthRange.length; ++i) {
  if (widthRange[i] != null) {
    // do something
  }
}

Вы не можете получить только те ключи, которые вы установили, потому что это не так, как работает Array. После того, как вы установили элемент 46, у вас также будет установлено значение от 0 до 45 (хотя они равны нулю).

У вас всегда может быть два массива:

var widthRange = [], widths = [], newVal = function(n) {
  widths.push(n);
  return n;
};
widthRange[newVal(26)] = { whatever: "hello there" };

for (var i = 0; i < widths.length; ++i) {
  doSomething(widthRange[widths[i]]);
}

редактировать ну может быть я тут весь мокрый ...


Массивы отслеживают только длину, они не проходят и не создают сколь угодно много объектов. В основном, когда новый индекс найден, он проверяет, превышает ли этот новый индекс длину ... если это новый индекс, становится новой длиной. Из-за этого можно получить только те индексы, которые вы определили с помощью цикла for-in
Боб,


1

Ваш оригинальный пример отлично подходит для меня:

<html>
<head>
</head>
<body>
<script>
var widthRange = new Array();
widthRange[46] = { sel:46, min:0,  max:52 };
widthRange[66] = { sel:66, min:52, max:70 };
widthRange[90] = { sel:90, min:70, max:94 };

var i = 1;
for (var key in widthRange)
{
    document.write("Key #" + i + " = " + key + "; &nbsp;&nbsp;&nbsp; min/max = " + widthRange[key].min + "/" + widthRange[key].max + "<br />");
    i++;
}
</script>
</html>

Результаты в браузере (Firefox 3.6.2 в Windows XP):

Key #1 = 46;     min/max = 0/52
Key #2 = 66;     min/max = 52/70
Key #3 = 90;     min/max = 70/94

2
Бьюсь об заклад, он импортирует Prototype или что-то в этом роде. В общем, входящие петли опасны.
Pointy

Ну, я использую jQuery, но также и Joomla, которая использует mootools, что-то добавляет эти свойства.
DisgruntledGoat

1

Я думаю, вам следует использовать для этого Object ( {}), а не array ( []).

С каждым ключом связан набор данных. Он кричит об использовании объекта. Делать:

var obj = {};
obj[46] = { sel:46, min:0,  max:52 };
obj[666] = { whatever:true };

// This is what for..in is for
for (var prop in obj) {
  console.log(obj[prop]);
}

Может быть, поможет какая-нибудь утилита вроде этой:

window.WidthRange = (function () {
  var obj = {};
  return {
    getObj: function () {return obj;}
    , add: function (key, data) {
        obj[key] = data;
        return this; // enabling chaining
      }
  }
})();

// Usage (using chaining calls):
WidthRange.add(66, {foo: true})
.add(67, {bar: false})
.add(69, {baz: 'maybe', bork:'absolutely'});

var obj = WidthRange.getObj();
for (var prop in obj) {
  console.log(obj[prop]);
}

0

Кажется, работает.

var widthRange = new Array();
widthRange[46] = { sel:46, min:0,  max:52 };
widthRange[66] = { sel:66, min:52, max:70 };
widthRange[90] = { sel:90, min:70, max:94 };

for (var key in widthRange)
{
    document.write(widthRange[key].sel + "<br />");
    document.write(widthRange[key].min + "<br />");
    document.write(widthRange[key].max + "<br />");
}

0

Я написал функцию, которая отлично работает с каждым экземпляром объектов (это массивы).

Object.prototype.toArray = function()
{
    if(!this)
    {
      return null;
    }

    var c = [];

    for (var key in this) 
    {
        if ( ( this instanceof Array && this.constructor === Array && key === 'length' ) || !this.hasOwnProperty(key) ) 
        {
            continue;
        }

        c.push(this[key]);
    }

    return c;
};

Применение:

var a   = [ 1, 2, 3 ];
a[11]   = 4;
a["js"] = 5;

console.log(a.toArray());

var b = { one: 1, two: 2, three: 3, f: function() { return 4; }, five: 5 };
b[7] = 7;

console.log(b.toArray());

Вывод:

> [ 1, 2, 3, 4, 5 ]
> [ 7, 1, 2, 3, function () { return 4; }, 5 ]

Может быть полезно кому угодно.


2
Расширение прототипа, который вы не создавали, - очень плохая идея, какой бы удобной она вам ни казалась.
doug65536

0

... ????

В качестве альтернативы, если у вас есть список элементов, которые вы хотите использовать ...

var range = [46, 66, 90]
    , widthRange=[]
    , write=[];

    widthRange[46] = { min:0, max:52 }; 
    widthRange[66] = { min:52, max:70 }; 
    widthRange[90] = { min:70, max:94 }; 

for(var x=0; x<range.length; x++){var key, wr;

    key = range[x];

    wr = widthRange[key] || false;

    if(wr===false){continue;}

    write.push(['key: #',key, ', min: ', wr.min, 'max:', wr.max].join(''));

    }

0

Для ваших входных данных:

let widthRange = new Array()
widthRange[46] = { min:0,  max:52 }
widthRange[61] = { min:52, max:70 }
widthRange[62] = { min:52, max:70 }
widthRange[63] = { min:52, max:70 }
widthRange[66] = { min:52, max:70 }
widthRange[90] = { min:70, max:94 }

Декларативный подход:

const relevantKeys = [46,66,90]
const relevantValues = Object.keys(widthRange)
    .filter(index => relevantKeys.includes(parseInt(index)))
    .map(relevantIndex => widthRange[relevantIndex])

Object.keysчтобы получить ключи, используя parseIntдля преобразования их в числа. filterполучить только те, которые вам нужны. mapдля создания массива из исходного объекта только тех индексов, которые вам нужны, поскольку Object.keysтеряет значения объекта.

Отлаживать:

console.log(widthRange)
console.log(relevantKeys)
console.log(relevantValues)

0

Вопрос довольно старый, но в настоящее время вы можете использовать forEach, который эффективен и сохраняет ключи в виде чисел:

let keys = widthRange.map((v,k) => k).filter(i=>i!==undefined))

Это проходит через widthRange и создает новый массив со значением ключей, а затем отфильтровывает все разреженные слоты, принимая только те значения, которые определены.

(Плохая идея, но для убедительности: если слот 0 всегда был пуст, его можно сократить до filter(i=>i)илиfilter(Boolean)

И это может быть менее эффективно, но числа можно преобразовать с помощью let keys = Object.keys(array).map(i=>i*1)

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.