Як отримати ключі масиву в 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
Справді? Ого, я що-небудь тут чомусь вчусь, і так часто це від вас Слакс :-) Я здивований, що Array так відстежує свої явно встановлені індекси. Може, я не мав би бути.
Пойнті

@SLaks: Дякую, зміна на об’єкт здається найкращим рішенням. Ще одне питання: чи є спосіб ітерації у зворотному порядку?
DisgruntledGoat

Ні; вам потрібно буде зробити це самостійно.
Слакс

2
Навіщо потрібен окремий чек length? Чи не позначено як [[DontEnum]]у всіх браузерах?
Roatin Marth

@Roatin: Я не знаю. Краще перестрахуватися, ніж потім шкодувати.
Слакс

84

Стрифіковані ключі можна запитувати за допомогою Object.keys(array).


4
Це повертає ключі у вигляді рядків, а не цифр, щоб усі були в курсі.
Хатч Мур

Найкраще рішення ... TY
Карлос Галеано,

Зробив мій день! : D <3 Це вирішило, де array.keys()нічого не повернуто
Крістофер Сток

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

редагувати добре може бути, що я тут увесь мокрий ...


Масиви відстежують лише довжину, вони не проходять і не створюють стільки об’єктів. В основному, коли новий індекс знайдений, він перевіряє, чи не перевищує цей новий індекс довжину ... якщо новий індекс стає новою довжиною. Через це можна отримати лише індекси, визначені вами за допомогою вхідного циклу
Bob


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
Б'юсь об заклад, він імпортує прототип або щось подібне. Загалом, ті, що входять у цикли, є ризикованими.
Пойнті

Ну, я використовую jQuery, але також Joomla, який використовує mootools, щось додає ці властивості.
DisgruntledGoat

1

Я думаю, вам слід використовувати Object ( {}), а не масив ([] ).

Набір даних пов'язаний з кожною клавішею. Він кричить, що використовує предмет. Зробіть:

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.