Jak uzyskać klucze tablicowe w JavaScript?

80

Mam tablicę utworzoną za pomocą tego kodu:

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

Chcę uzyskać każdą z wartości 46, 66, 90 w pętli. Próbowałem, for (var key in widthRange)ale to daje mi całą masę dodatkowych właściwości (zakładam, że są to funkcje na obiekcie). Nie mogę użyć zwykłej pętli for, ponieważ wartości nie są sekwencyjne.

Niezadowolony Kozioł
źródło
9
Wygląda na to, że masz dane, które, chociaż mają klucze numeryczne, w rzeczywistości nie są danymi tablicowymi. Spojrzałbym tutaj na użycie zwykłego przedmiotu.
Quentin,
@Quentin To się nazywa rzadka tablica. Z punktu widzenia wydajności najlepiej jest tutaj użyć tablicy zamiast obiektu. Ponadto, wydajność mądry, najlepszą odpowiedzią nie jest nawet wymienione: Array.prototype.forEach. Wywołanie Object.keystablicy nie działa, ponieważ przeglądarki nie optymalizują jej pod kątem. for(var key in array)jest zły, ponieważ przechodzi przez prototyp i określa łańcuchy każdego napotkanego klucza liczbowego (konwersja podwójnych liczb do base10 jest bardzo powolna). forEachjednak został zaprojektowany dokładnie w celu iteracji rzadkich tablic i zapewni Twojemu kodowi doskonałą wydajność w porównaniu z innymi rozwiązaniami
Jack Giffin

Odpowiedzi:

96

Musisz wywołać hasOwnPropertyfunkcję, aby sprawdzić, czy właściwość jest faktycznie zdefiniowana na samym obiekcie (w przeciwieństwie do jego prototypu), na przykład:

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

Pamiętaj, że potrzebujesz osobnego czeku na length.
Jednak w ogóle nie powinieneś używać tutaj tablicy; powinieneś użyć zwykłego przedmiotu. Wszystkie obiekty JavaScript działają jako tablice asocjacyjne.

Na przykład:

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 };
SLaks
źródło
2
Naprawdę? Wow, uczę się tu czegoś codziennie i tak często to od Was, SLaków :-) Dziwię się, że Array śledzi w ten sposób swoje wyraźnie ustawione indeksy. Może nie powinienem.
Pointy
@SLaks: Dzięki, zmiana go na obiekt wydaje się najprzyjemniejszym rozwiązaniem. Jeszcze jedno pytanie: czy istnieje sposób na iterację w odwrotnej kolejności?
DisgruntledGoat
Nie; musisz to zrobić sam.
SLaks
2
Dlaczego potrzebujesz osobnego czeku length? Czy nie jest zaznaczone tak, jak [[DontEnum]]we wszystkich przeglądarkach?
Roatin Marth
@Roatin: Nie wiem. Lepiej dmuchać na zimne.
SLaks
84

Można zapytać o klucze strunowe Object.keys(array).

thSoft
źródło
4
Zwraca klucze jako łańcuchy, a nie liczby, aby wszyscy byli świadomi.
Hutch Moore
Najlepsze rozwiązanie ... TY
Carlos Galeano
Zrobiłem mój dzień! : D <3 To rozwiązało problem, gdzie array.keys()nic nie zwróciło.
Christopher Stock,
18

Jeśli wykonujesz jakąkolwiek operację lub kontrolę tablicy / kolekcji, bardzo polecam użycie Underscore.js . Jest mały, dobrze przetestowany i pozwoli Ci zaoszczędzić dni / tygodnie / lata bólu głowy w języku JavaScript. Oto jego funkcja klawiszy:

Klucze

Pobierz wszystkie nazwy właściwości obiektu.

_.keys({one : 1, two : 2, three : 3});
=> ["one", "two", "three"]
Mike McKay
źródło
4

Powiedz, że twoja tablica wygląda jak arr = [ { a: 1, b: 2, c: 3 }, { a: 4, b: 5, c: 6 }, { a: 7, b: 8, c: 9 } ](lub prawdopodobnie inne klucze), które możesz zrobić

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"]

Morten
źródło
3
for (var i = 0; i < widthRange.length; ++i) {
  if (widthRange[i] != null) {
    // do something
  }
}

Tak naprawdę nie możesz uzyskać tylko ustawionych kluczy, ponieważ nie tak działa tablica. Po ustawieniu elementu 46 masz również ustawione od 0 do 45 (chociaż są one zerowe).

Zawsze możesz mieć dwie tablice:

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

edytuj dobrze, może być tak, że jestem tu cała mokra

Spiczasty
źródło
Tablice śledzą tylko długość, nie przechodzą i nie tworzą zbyt wielu obiektów. Zasadniczo, gdy zostanie znaleziony nowy indeks, sprawdza, czy ten nowy indeks jest większy niż długość ... jeśli jest to ten nowy indeks staje się nową długością. Dzięki temu możliwe jest pobranie tylko indeksów, które zdefiniowałeś za pomocą pętli for-in
Bob
2
widthRange.map(function(_, i) { return i });

lub

widthRange.map((_, i) => i);
alexndreazevedo
źródło
1

Twój oryginalny przykład działa dobrze dla mnie:

<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>

Wyniki w przeglądarce (Firefox 3.6.2 w systemie Windows XP):

Key #1 = 46;     min/max = 0/52
Key #2 = 66;     min/max = 52/70
Key #3 = 90;     min/max = 70/94
Andy Shellam
źródło
2
Założę się, że importuje Prototype lub coś w tym rodzaju. Ogólnie rzecz biorąc, pętle „w” są ryzykowne.
Pointy
Cóż, używam jQuery, ale także Joomla, która używa mootools, coś dodaje te właściwości.
DisgruntledGoat
1

Myślę, że powinieneś użyć Object ( {}), a nie tablicy ([] ).

Z każdym kluczem powiązany jest zestaw danych. Krzyczy za użycie przedmiotu. Zrobić:

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

Może takie narzędzia mogą pomóc:

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]);
}
npup
źródło
0

Wydaje się działać.

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 />");
}
Michael D. Irizarry
źródło
0

Napisałem funkcję, która działa dobrze z każdą instancją obiektów (to są tablice).

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

Stosowanie:

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());

Wynik:

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

Może się przydać każdemu.

schwarzkopfb
źródło
2
Przedłużenie prototypu, którego nie stworzyłeś, jest bardzo złym pomysłem, bez względu na to, jak wygodne jest to dla ciebie.
doug65536,
0

... ????

Alternatywnie, jeśli masz listę przedmiotów, których chcesz użyć ...

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(''));

    }
Yann Carter
źródło
0

Twoje dane wejściowe:

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 }

Podejście deklaratywne:

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

Object.keysaby zdobyć klucze, używając, parseIntaby rzucić je jako liczby. filterdostać tylko te, które chcesz. mapaby zbudować tablicę z oryginalnego obiektu tylko indeksów, których szukasz, ponieważObject.keys traci wartości obiektu.

Odpluskwić:

console.log(widthRange)
console.log(relevantKeys)
console.log(relevantValues)
S ..
źródło
0

Pytanie jest dość stare, ale obecnie możesz użyć forEach, który jest wydajny i zachowuje klucze jako liczby:

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

Ta pętla przechodzi przez widthRange i tworzy nową tablicę z wartościami kluczy, a następnie odfiltrowuje wszystkie rzadkie szczeliny, biorąc tylko wartości, które są zdefiniowane.

(Zły pomysł, ale ze względu na wytrzymałość: jeśli slot 0 był zawsze pusty, można to skrócić do filter(i=>i)lubfilter(Boolean)

I może być mniej wydajne, ale liczby można rzucać za pomocą let keys = Object.keys(array).map(i=>i*1)

SamGoody
źródło