Wie bekomme ich Array-Schlüssel in Javascript?


80

Ich habe ein Array mit diesem Code erstellt:

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

Ich möchte jeden der Werte 46, 66, 90 in einer Schleife erhalten. Ich habe es versucht, for (var key in widthRange)aber dies gibt mir eine ganze Reihe zusätzlicher Eigenschaften (ich gehe davon aus, dass es sich um Funktionen des Objekts handelt). Ich kann keine reguläre for-Schleife verwenden, da die Werte nicht sequentiell sind.


9
Es sieht so aus, als hätten Sie Daten, die zwar Zifferntasten haben, aber eigentlich keine Array-Daten sind. Ich würde hier ein normales Objekt verwenden.
Quentin

@ Quentin Es heißt ein spärliches Array. In Bezug auf die Leistung ist es am besten, hier ein Array anstelle eines Objekts zu verwenden. Auch in Bezug auf die Leistung ist die beste Antwort nicht einmal aufgeführt : Array.prototype.forEach. Das Aufrufen Object.keyseines Arrays ist schlecht, da Browser nicht dafür optimieren. for(var key in array)ist schlecht, weil es den Prototyp durchquert und jeden Ziffernschlüssel, auf den es trifft, stringifiziert (das Konvertieren von Doubles in base10 ist sehr langsam). forEachEs wurde jedoch genau zum Zweck der Iteration von Arrays mit geringer Dichte entwickelt und bietet Ihrem Code im Vergleich zu anderen Lösungen eine hervorragende Leistung
Jack Giffin,

Antworten:


96

Sie müssen die hasOwnPropertyFunktion aufrufen , um zu überprüfen, ob die Eigenschaft tatsächlich für das Objekt selbst definiert ist (im Gegensatz zu seinem Prototyp).

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

Beachten Sie, dass Sie eine separate Prüfung für benötigen length.
Sie sollten hier jedoch überhaupt kein Array verwenden. Sie sollten ein reguläres Objekt verwenden. Alle Javascript-Objekte fungieren als assoziative Arrays.

Zum Beispiel:

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
"Ja wirklich?" Wow, ich lerne hier jeden Tag etwas und so oft ist es von dir SLaks :-) Ich bin überrascht, dass Array seine explizit festgelegten Indizes so verfolgt. Vielleicht sollte ich nicht sein.
Pointy

@SLaks: Danke, es scheint die schönste Lösung zu sein, es in ein Objekt zu ändern. Noch eine Frage: Gibt es eine Möglichkeit, in umgekehrter Reihenfolge zu iterieren?
DisgruntledGoat

Nein; Sie müssen es selbst tun.
SLaks

2
Warum benötigen Sie einen separaten Scheck für length? Ist nicht wie [[DontEnum]]in allen Browsern markiert ?
Roatin Marth

@ Roatin: Ich weiß es nicht. Sicher ist sicher.
SLaks


18

Wenn Sie irgendeine Art von Manipulation oder Inspektion von Arrays / Sammlungen durchführen, empfehle ich dringend die Verwendung von Underscore.js . Es ist klein, gut getestet und erspart Ihnen Tage / Wochen / Jahre Javascript-Kopfschmerzen. Hier ist seine Tastenfunktion:

Schlüssel

Rufen Sie alle Namen der Objekteigenschaften ab.

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

4

Angenommen, Ihr Array sah aus wie arr = [ { a: 1, b: 2, c: 3 }, { a: 4, b: 5, c: 6 }, { a: 7, b: 8, c: 9 } ](oder möglicherweise andere Schlüssel), die Sie ausführen könnten

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

Sie können nicht wirklich nur die Schlüssel erhalten, die Sie festgelegt haben, da ein Array so nicht funktioniert. Sobald Sie Element 46 gesetzt haben, haben Sie auch 0 bis 45 gesetzt (obwohl sie null sind).

Sie können immer zwei Arrays haben:

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

gut bearbeiten es kann sein, dass ich hier ganz nass bin ...


Arrays verfolgen nur die Länge, sie gehen nicht durch und erstellen so viele Objekte wie möglich. Wenn ein neuer Index gefunden wird, prüft er grundsätzlich, ob dieser neue Index größer als die Länge ist ... wenn dieser neue Index zur neuen Länge wird. Aus diesem Grund ist es möglich, nur die Indizes abzurufen, die Sie mit einer For-In-Schleife definiert haben
Bob


1

Ihr ursprüngliches Beispiel funktioniert gut für mich:

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

Ergebnisse im Browser (Firefox 3.6.2 unter Windows XP):

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

2
Ich wette, er importiert Prototype oder so etwas. Im Allgemeinen sind diese "In" -Schleifen riskant.
Pointy

Nun, ich verwende jQuery, aber auch Joomla, das Mootools verwendet. Etwas fügt diese Eigenschaften hinzu.
DisgruntledGoat

1

Ich denke, Sie sollten dafür ein Object ( {}) und kein Array ( []) verwenden.

Jedem Schlüssel ist ein Datensatz zugeordnet. Es schreit nach einem Objekt. Tun:

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

Vielleicht können solche Hilfsprogramme helfen:

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

Scheint zu funktionieren.

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

Ich habe eine Funktion geschrieben, die mit jeder Instanz von Objekten gut funktioniert (Arrays sind solche).

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

Verwendung:

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

Ausgabe:

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

Es kann für jeden nützlich sein.


2
Das Erweitern eines Prototyps, den Sie nicht erstellt haben, ist eine sehr schlechte Idee, egal wie bequem Sie es finden.
Doug65536

0

... ????

Alternativ, wenn Sie eine Liste von Elementen haben, die Sie verwenden möchten ...

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

Für Ihre Eingabedaten:

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 }

Deklarativer Ansatz:

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

Object.keysum die Schlüssel zu erhalten, verwenden Sie parseIntsie als Zahlen. filterum nur diejenigen zu bekommen, die Sie wollen. mapum ein Array aus dem ursprünglichen Objekt nur der Indizes zu erstellen, nach denen Sie suchen, da Object.keysdie Objektwerte verloren gehen.

Debuggen:

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

0

Die Frage ist ziemlich alt, aber heutzutage können Sie forEach verwenden, was effizient ist und die Schlüssel als Zahlen behält:

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

Dies durchläuft widthRange und erstellt ein neues Array mit dem Wert der Schlüssel. Anschließend werden alle Sparce-Slots herausgefiltert, indem nur die definierten Werte verwendet werden.

(Schlechte Idee, aber aus Gründen der Gründlichkeit: Wenn Steckplatz 0 immer leer war, könnte dies auf filter(i=>i)oder verkürzt werdenfilter(Boolean)

Und es mag weniger effizient sein, aber die Zahlen können mit gegossen werden let keys = Object.keys(array).map(i=>i*1)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.