Является ли массив одновременно ассоциативным и индексированным?

Может ли массив в JavaScript быть ассоциативным и проиндексированным?

Я хотел бы иметь возможность искать элемент в массиве по его положению или значению ключа.


person puffpio    schedule 02.07.2009    source источник
comment
это довольно давно, но вы можете использовать Object.keys (array), чтобы получить ключи по порядку   -  person Tom Prats    schedule 30.10.2013
comment
Вы программист на PHP :)   -  person nawfal    schedule 12.11.2015


Ответы (11)


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

Например, с этим объектом:

var params = {
    foo: 1,
    bar: 0,
    other: 2
};

Вы можете получить доступ к свойствам из объекта, например:

params["foo"];

И вы также можете перебирать объект с помощью оператора for...in:

for(var v in params) {
    //v is equal to the currently iterated property
}

Однако нет строгого правила в отношении порядка итерации свойств - две итерации литерала вашего объекта могут возвращать свойства в разном порядке.

person Alex Rozanski    schedule 02.07.2009
comment
На самом деле массивы JavaScript также могут иметь нецелочисленные индексы. У него просто не так много способов справиться с ними. - person Nosredna; 03.07.2009
comment
Вы имеете в виду, как струна? в этом случае это не массив, а просто объект со свойствами. - person roryf; 03.07.2009
comment
Nosredna: массивы JS не имеют ключевых индексов, эти объекты JS считаются объектными литералами. итак: foo [bar] = 1; foo.bar === foo [бар]; // верно foo [bar] === foo [0]; // false Это одна из многих тонких причуд JS, которые легко сбивают с толку. - person linusthe3rd; 03.07.2009
comment
Объекты JavaScript, включая массивы, имеют только строковые индексы. a[1] на самом деле говорит a['1']. - person bobince; 05.04.2010
comment
В последней документации MDN ясно сказано, что индекс массива должен быть целым. developer.mozilla.org/en-US/docs/ Интернет / JavaScript / Справочные материалы / - person gujralam; 06.09.2020

Прочитав определение ассоциативного массива в Википедии, я собираюсь порвать с традиционными знаниями JavaScript и сказать , "да, в JavaScript есть ассоциативные массивы." С помощью массивов JavaScript вы можете добавлять, переназначать, удалять и искать значения по их ключам (а ключи могут быть строками в кавычках), что, по словам Википедии, должно быть у ассоциативных массивов.

Однако вы, кажется, спрашиваете о другом - можете ли вы найти одно и то же значение по индексу или ключу. Это не требование ассоциативных массивов (см. Статью в Википедии). Ассоциативные массивы не должны давать вам возможность получать значение по индексу.

Массивы JavaScript очень похожи на объекты JavaScript.

  arr=[];
  arr[0]="zero";
  arr[1]="one";
  arr[2]="two";
  arr["fancy"]="what?";

Да, это массив, и да, вы можете обойтись нечисловыми индексами. (Если вам любопытно, после всего этого приблизительная длина равна 3.)

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

Ссылка на мой пост в блоге по этой теме.

person Nosredna    schedule 02.07.2009
comment
Вы были правы, когда сказали, что у вас может быть массив с нечисловыми индексами. Это как-то выскользнуло из головы, но я знаю этот факт. - person Ionuț G. Stan; 03.07.2009
comment
Чтобы быть педантичным, fancy - это не индекс в массиве, а атрибут объекта экземпляра массива. - person BaroqueBobcat; 03.07.2009
comment
Да, хорошее замечание. Вот почему я говорю, что массивы тесно связаны с объектами в JavaScript. - person Nosredna; 03.07.2009
comment
@BaroqueBobcat: если быть действительно педантичным :), индексы в массиве - это просто свойства (атрибуты) объекта экземпляра массива; массив просто обрабатывает специально те свойства, которые являются строковой формой целого числа в отношении свойства длины. - person Miles; 03.07.2009
comment
Это совсем не педантично. Это невероятно важное различие, которое становится очевидным, когда вы начинаете пытаться сериализовать свой массив, скажем, в JSON. Значение с нечисловым ключом не является частью данных массива. (И, строго говоря, вы нарушаете свой массив, не используя .push().) - person Lightness Races in Orbit; 04.08.2011
comment
Я отклонил этот ответ, потому что, хотя можно было бы использовать синтаксис, который выглядит как массив, это не правильный javascript или правильный массив. Ваш ответ хорош для справки, но должен иметь четкое напоминание о том, что это НЕ то, как вы обычно используете массивы. - person kontur; 10.08.2012
comment
Массивы должны иметь возможность индексироваться, чтобы при повторении они возвращались по порядку, но здесь это не так. - person z.a.; 12.12.2015
comment
Я знаю, что это старый пост, но для ясности: в JavaScript нет ассоциативных массивов, и вы не можете передать строковый индекс в массив. Речь идет об объекте с ключами. - person Scott Marcus; 29.09.2020

Собственные объекты JS принимают только строки в качестве имен свойств, что верно даже для числовых индексы массива; массивы отличаются от обычных объектов только постольку, поскольку большинство реализаций JS по-разному хранят свойства с числовой индексацией (т.е. в реальном массиве, пока они плотные), и их установка вызовет дополнительные операции (например, настройку свойства length).

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

Простая реализация карты, которая будет делать то, что вы просите, может выглядеть так:

function Map() {
    this.length = 0;
    this.store = {};
}

Map.prototype.get = function(key) {
    return this.store.hasOwnProperty(key) ?
        this.store[key] : undefined;
};

Map.prototype.put = function(key, value, index) {
    if(arguments.length < 3) {
        if(this.store.hasOwnProperty(key)) {
            this.store[key].value = value;
            return this;
        }

        index = this.length;
    }
    else if(index >>> 0 !== index || index >= 0xffffffff)
        throw new Error('illegal index argument');

    if(index >= this.length)
        this.length = index + 1;

    this[index] = this.store[key] =
        { index : index, key : key, value : value };

    return this;
};

Аргумент index для put() не является обязательным.

Вы можете получить доступ к значениям на карте map либо по ключу, либо по индексу через

map.get('key').value
map[2].value
person Christoph    schedule 02.07.2009
comment
+1, но я бы потерял численно проиндексированные свойства, которые будут храниться по-другому: это не обязательно так и не требуется спецификацией. - person Miles; 03.07.2009
comment
Как поставить? map.put ('ключ', 'значение') и map [2] .value = val? - person Chris; 14.01.2011
comment
map.put('key','value') для добавления значения и map.put('key','value', 42) для вставки значения в заданную позицию; map[42].value = 'value' также будет работать (т.е. заменить значение существующей пары ключ-значение), но вы не можете изменить ключ или индекс записи или добавить новые записи таким образом - person Christoph; 15.01.2011

var myArray = Array();
myArray["first"] = "Object1";
myArray["second"] = "Object2";
myArray["third"] = "Object3";

Object.keys(myArray);              // returns ["first", "second", "third"]
Object.keys(myArray).length;       // returns 3

если вам нужен первый элемент, вы можете использовать его так:

myArray[Object.keys(myArray)[0]];  // returns "Object1"
person Tom Prats    schedule 30.10.2013

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

РЕДАКТИРОВАТЬ:

как указывает Perspx, на самом деле в javascript нет настоящих ассоциативных массивов. Оператор foo["bar"] - это просто синтаксический сахар для foo.bar

Если вы доверяете браузеру поддерживать порядок элементов в объекте, вы можете написать функцию

function valueForIndex(obj, index) {

    var i = 0;
    for (var key in obj) {

        if (i++ == index)
            return obj[key];
    }
}
person Matt Bridges    schedule 02.07.2009
comment
Хотя это правда, на практике все основные браузеры перебирают свойства объекта в том порядке, в котором они были определены. Этого, конечно, нет в спецификации, но стоит упомянуть. - person Paolo Bergantino; 03.07.2009
comment
В JavaScript нет ассоциативных массивов. Ваш ответ касается объектов, а не массивов. - person Scott Marcus; 29.09.2020

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

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

На практике это довольно просто, но я до сих пор ничего не читал о том, ужасная это практика или нет.

var roygbiv = [];
var colour = { key : "red", hex : "#FF0000" };
roygbiv.push(colour);
roygbiv[colour.key] = colour;
...
console.log("Hex colours of the rainbow in order:");
for (var i = 0; i < roygbiv.length; i++) {
    console.log(roygbiv[i].key + " is " + roygbiv[i].hex);
}

// input = "red";
console.log("Hex code of input colour:");
console.log(roygbiv[input].hex);

Важно никогда не изменять значение array [index] или array [key] сразу после установки объекта, иначе значения больше не будут совпадать. Если массив содержит объекты, вы можете изменить свойства этих объектов, и вы сможете получить доступ к измененным свойствам любым из методов.

person Joel Roberts    schedule 21.03.2016
comment
Я выбрал именно такой подход, но с синтаксисом, показывающим связь при назначении, который мне больше нравится aSignals[aSignals.length] = aSignals['fooBar'] = { 'sKey': 'foobBar', ... } - person Daniel Sokolowski; 24.04.2018

Хотя я согласен с приведенными ответами, вы действительно можете выполнить то, что говорите, с помощью геттеров и сеттеров. Например:

var a = [1];
//This makes a["blah"] refer to a[0]
a.__defineGetter__("blah", function(){return this[0]});
//This makes a["blah"] = 5 actually store 5 into a[0]
a.__defineSetter__("blah", function(val){ this[0] = val});

alert(a["blah"]); // emits 1
a["blah"] = 5;
alert(a[0]); // emits 5

Это то, что вы ищите? Я думаю, что есть другой, более современный способ делать геттеры и сеттеры, но не могу вспомнить.

person Ryan    schedule 13.04.2012
comment
О, и если вы собираетесь делать это в цикле, вам, вероятно, понадобятся замыкания, кстати ... просто скажу. - person Ryan; 14.04.2012

На этом прилив изменился. Теперь вы можете это сделать ... и БОЛЬШЕ! Используя Harmony Proxies, вы определенно можете решить эту проблему разными способами.

Вам нужно будет убедиться, что ваши целевые среды поддерживают это, возможно, с небольшой помощью harmony-reflection прокладка.

В сети разработчиков Mozilla есть действительно хороший пример использования прокси для найти объект элемента массива по его свойству, что в значительной степени подытоживает его.

Вот моя версия:

  var players = new Proxy(
  [{
    name: 'monkey',
    score: 50
  }, {
    name: 'giraffe',
    score: 100
  }, {
    name: 'pelican',
    score: 150
  }], {
    get: function(obj, prop) {
      if (prop in obj) {
        // default behavior
        return obj[prop];
      }
      if (typeof prop == 'string') {

        if (prop == 'rank') {
          return obj.sort(function(a, b) {
            return a.score > b.score ? -1 : 1;
          });
        }

        if (prop == 'revrank') {
          return obj.sort(function(a, b) {
            return a.score < b.score ? -1 : 1;
          });
        }

        var winner;
        var score = 0;
        for (var i = 0; i < obj.length; i++) {
          var player = obj[i];
          if (player.name == prop) {
            return player;
          } else if (player.score > score) {
            score = player.score;
            winner = player;
          }
        }

        if (prop == 'winner') {
          return winner;
        }
        return;
      }

    }
  });

  console.log(players[0]); // { name: 'monkey', score: 50 }
  console.log(players['monkey']); // { name: 'monkey', score: 50 }
  console.log(players['zebra']); // undefined
  console.log(players.rank); // [ { name: 'pelican', score: 150 },{ name: 'giraffe', score: 100 }, { name: 'monkey', score: 50 } ]
  console.log(players.revrank); // [ { name: 'monkey', score: 50 },{ name: 'giraffe', score: 100 },{ name: 'pelican', score: 150 } ]
  console.log(players.winner); // { name: 'pelican', score: 150 }
person Quickredfox    schedule 09.04.2016

В последней документации MDN ясно сказано, что индекс массива должен быть целым. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array

let arr=[];
 arr[0]="zero";
 arr[1]="one";
 arr[2]="two";
 arr["fancy"]="what?";

//Arrays cannot use strings as element indexes (as in an associative array) but must use integers. 
//Setting non-integers using bracket notation will not set an element to the Array List itself
//A non-integer will set a variable associated with that ARRAY Object property collection 
let denseKeys = [...arr.keys()];
console.log(denseKeys);//[ 0, 1, 2 ]
console.log("ARRAY Keys:"+denseKeys.length);//3


let sparseKeys = Object.keys(arr);
console.log(sparseKeys);//[ '0', '1', '2', 'fancy' ]
console.log("Object Keys:"+sparseKeys.length);//4

const iterator = arr.keys();
for (const key of iterator) {
  console.log(key);//0,1,2
}
person gujralam    schedule 06.09.2020

да.

test = new Array();
test[0] = 'yellow';
test['banana'] = 0;
alert(test[test['banana']]);
person Todd Moyér    schedule 21.02.2012

person    schedule
comment
но тогда stuff.length больше не будет 1 с тех пор, как вы добавили .bar, верно? таким образом, перебор по индексу больше не будет работать .. - person puffpio; 10.07.2009
comment
Нет - добавление именованного свойства не увеличивает длину; только добавление элемента по числовому индексу увеличивает длину. Добавление stuff [1000] = 'blah' приведет к увеличению длины до 1001, хотя фактически были добавлены только два элемента с числовой индексацией и одно именованное свойство. Забавно, не правда ли ;-) - person NickFitz; 10.07.2009