Jak przejść przez zwykły obiekt JavaScript z obiektami jako członkami?

1599

Jak mogę zapętlić wszystkie elementy w obiekcie JavaScript, w tym wartości będące obiektami.

Na przykład, w jaki sposób mogę przez to przejść (uzyskując dostęp do „nazwa_użytkownika” i „nazwa_użytkownika” dla każdego z nich)?

var validation_messages = {
    "key_1": {
        "your_name": "jimmy",
        "your_msg": "hello world"
    },
    "key_2": {
        "your_name": "billy",
        "your_msg": "foo equals bar"
    }
}
edt
źródło
11
możliwy duplikat Pętli przez obiekt JavaScript
BuZZ-dEE,

Odpowiedzi:

2112
for (var key in validation_messages) {
    // skip loop if the property is from prototype
    if (!validation_messages.hasOwnProperty(key)) continue;

    var obj = validation_messages[key];
    for (var prop in obj) {
        // skip loop if the property is from prototype
        if (!obj.hasOwnProperty(prop)) continue;

        // your code
        alert(prop + " = " + obj[prop]);
    }
}
AgileJon
źródło
13
Internet Explorer nie zgadza się ( wzdycha ), mówi „Obiekt nie obsługuje tej właściwości lub metody” podczas wykonywania obj [prop]. Muszę jeszcze znaleźć rozwiązanie tego problemu.
user999717,
2
@MildFuzz ma sens, jeśli weźmiesz pod uwagę, że obiekty JS nie muszą mieć klawiszy numerycznych. Nie możesz po prostu iterować przez obiekt. JS for injest bardzo podobny do tradycyjnego foreach.
Jake Wilson
4
for ... in jest dobrym rozwiązaniem, ale jeśli używasz obietnic w pętli for (), zachowaj ostrożność, ponieważ jeśli utworzysz var w pętli, nie będziesz mógł jej użyć w funkcji obietnicy. Twoja zmienna w pętli istnieje tylko raz, więc ma w każdej funkcji wtedy tę samą, nawet ostatnią wartość. Jeśli masz ten problem, wypróbuj „Object.keys (obj) .forEach” lub moją odpowiedź poniżej.
Biber,
775

W ECMAScript 5 możesz łączyć Object.keys()i Array.prototype.forEach():

var obj = {
  first: "John",
  last: "Doe"
};

//
//	Visit non-inherited enumerable keys
//
Object.keys(obj).forEach(function(key) {

  console.log(key, obj[key]);

});

Axel Rauschmayer
źródło
34
+1 za zwięzłość kodu, ale najwyraźniej nie działa tak skutecznie jak zaskakująco. JSPerf - for in vs Object.keys
techiev2 2
6
Uważaj na ten błąd, stosując następujące podejście: „TypeError: Object.keys wywołany na obiekcie innym niż obiekt”. for ... in ... hasOwnPropertyWzór można nazwać na niczym, o ile mogę powiedzieć (obiekt, array, null, niezdefiniowane, true, false, numer prymitywne, obiekty).
theazureshadow
2
Zauważ, że IE7 tego nie obsługuje.
Paul D. Waite,
3
@ techiev2 te testy nigdy nie były ważne. Zobacz moje zaktualizowane aktualne wyniki: jsperf.com/objdir/20
OrganicPanda
4
@ techiev2: to nie Object.keys()jest powolne, to raczej ciągły forEach()dostęp do .length! Jeśli forzamiast tego użyjesz klasycznej pętli, w przeglądarce Firefox 33 jest ona prawie dwa razy szybsza niż for..in+.hasOwnProperty()
CodeManX,
384

Problem z tym

for (var key in validation_messages) {
   var obj = validation_messages[key];
   for (var prop in obj) {
      alert(prop + " = " + obj[prop]);
   }
}

polega na tym, że będziesz również przechodzić przez prototyp pierwotnego obiektu.

Dzięki temu unikniesz tego:

for (var key in validation_messages) {
   if (validation_messages.hasOwnProperty(key)) {
      var obj = validation_messages[key];
      for (var prop in obj) {
         if (obj.hasOwnProperty(prop)) {
            alert(prop + " = " + obj[prop]);
         }
      }
   }
}
Chango
źródło
46
W skrócie: sprawdzić hasOwnPropertywewnątrz firmy for- inpętle.
Rory O'Kane
59
Należy pamiętać, że jest to konieczne tylko wtedy, gdy obiekt HAS ma metody prototypowe. Na przykład, jeśli zapętlony obiekt jest tylko obiektem JSON, nie będziesz potrzebować tego sprawdzania.
gitaarik
6
@rednaw Dla bezpieczeństwa używam tego wyboru, ponieważ Object.prototype można modyfikować. Nie zrobiłby tego żaden rozsądny skrypt, ale nie możesz kontrolować, jakie skrypty mogą być uruchamiane na twojej stronie przez szalone rozszerzenia przeglądarki. Rozszerzenia przeglądarki działają na twojej stronie (w większości przeglądarek) i mogą powodować dziwne problemy (np. Ustaw window.setTimeout na zero!).
robocat,
1
Dziękuję bardzo
Blue Tram
328

W ES6 / 2015 możesz przechodzić przez taki obiekt: (używając funkcji strzałki )

Object.keys(myObj).forEach(key => {
  console.log(key);        // the name of the current key.
  console.log(myObj[key]); // the value of the current key.
});

jsbin

W ES7 / 2016 możesz używać Object.entrieszamiast tego Object.keysi przechodzić przez taki obiekt:

Object.entries(myObj).forEach(([key, val]) => {
  console.log(key); // the name of the current key.
  console.log(val); // the value of the current key.
});

Powyższe działałoby również jako jednolinijka :

Object.entries(myObj).forEach(([key, val]) => console.log(key, val));

jsbin

Jeśli chcesz również zapętlić zagnieżdżone obiekty, możesz użyć funkcji rekurencyjnej (ES6):

const loopNestedObj = obj => {
  Object.keys(obj).forEach(key => {
    if (obj[key] && typeof obj[key] === "object") loopNestedObj(obj[key]); // recurse.
    else console.log(key, obj[key]); // or do something with key and val.
  });
};

jsbin

Taki sam jak funkcja powyżej, ale z ES7 Object.entries() zamiast Object.keys():

const loopNestedObj = obj => {
  Object.entries(obj).forEach(([key, val]) => {
    if (val && typeof val === "object") loopNestedObj(val); // recurse.
    else console.log(key, val); // or do something with key and val.
  });
};

Tutaj przechodzimy przez obiekty zagnieżdżone, zmieniamy wartości i zwracamy nowy obiekt za jednym razem, używając w Object.entries()połączeniu z Object.fromEntries()( ES10 / 2019 ):

const loopNestedObj = obj =>
  Object.fromEntries(
    Object.entries(obj).map(([key, val]) => {
      if (val && typeof val === "object") [key, loopNestedObj(val)]; // recurse
      else [key, updateMyVal(val)]; // or do something with key and val.
    })
  );
Rotareti
źródło
2
dla Twojego ES7 na przykładzie Object.entries, musisz owinąć parametry funkcji strzałek [key, val] w nawiasach takich jak: `Object.entries (myObj) .forEach (([[key, val]) => instrukcje {/ * * /}
puiu
6
Myślę, że użyteczne byłoby dodanie faktu, że Object.entries i Object.keys nie iterują po prototypie, co stanowi dużą różnicę między nim a konstrukcją for.
steviejay
Dziękuję bardzo
Blue Tram
95

Korzystanie z Underscore.js_.each :

_.each(validation_messages, function(value, key){
    _.each(value, function(value, key){
        console.log(value);
    });
});
Tim Santeford
źródło
4
Dzięki Tim, użycie podkreślenia jest tak dobre, aby mieć szybką i czystą opcję.
The Coder
56

Jeśli użyjesz rekurencji, możesz zwrócić właściwości obiektu o dowolnej głębokości

function lookdeep(object){
    var collection= [], index= 0, next, item;
    for(item in object){
        if(object.hasOwnProperty(item)){
            next= object[item];
            if(typeof next== 'object' && next!= null){
                collection[index++]= item +
                ':{ '+ lookdeep(next).join(', ')+'}';
            }
            else collection[index++]= [item+':'+String(next)];
        }
    }
    return collection;
}

//example

var O={
    a:1, b:2, c:{
        c1:3, c2:4, c3:{
            t:true, f:false
        }
    },
    d:11
};
var lookdeepSample= 'O={'+ lookdeep(O).join(',\n')+'}';


/*  returned value: (String)
O={
    a:1, 
    b:2, 
    c:{
        c1:3, c2:4, c3:{
            t:true, f:false
        }
    },
    d:11
}

*/
Kennebec
źródło
2
Uwaga na pętle, takie jak wywoływanie tego w węźle DOM.
theazureshadow
45

Ta odpowiedź jest agregacją rozwiązań, które zostały dostarczone w tym poście z pewnymi sprzężeniami zwrotnymi dotyczącymi wydajności . Myślę, że istnieją 2 przypadki użycia, a OP nie wspomniał o tym, czy musi uzyskać dostęp do kluczy, aby użyć ich podczas procesu pętli.

I. klucze muszą być dostępne,

ofi Object.keyspodejście

let k;
for (k of Object.keys(obj)) {

    /*        k : key
     *   obj[k] : value
     */
}

inpodejście

let k;
for (k in obj) {

    /*        k : key
     *   obj[k] : value
     */
}

Używaj tego z ostrożnością, ponieważ może wydrukować właściwości prototypu obj

✔ podejście ES7

for (const [key, value] of Object.entries(obj)) {

}

Jednak podczas edycji nie polecałbym metody ES7, ponieważ JavaScript inicjuje wiele zmiennych wewnętrznie, aby zbudować tę procedurę (zobacz dowody zwrotne dla potwierdzenia). Jeśli nie tworzysz ogromnej aplikacji, która zasługuje na optymalizację, jest w porządku, ale jeśli optymalizacja jest twoim priorytetem, powinieneś o tym pomyśleć.

II. musimy tylko uzyskać dostęp do każdej wartości,

ofi Object.valuespodejście

let v;
for (v of Object.values(obj)) {

}

Więcej informacji zwrotnych na temat testów:

  • Buforowanie Object.keyslub Object.valueswydajność jest znikoma

Na przykład,

const keys = Object.keys(obj);
let i;
for (i of keys) {
  //
}
// same as
for (i of Object.keys(obj)) {
  //
}
  • Na Object.valuesprzykład użycie natywnej forpętli ze zmiennymi buforowanymi w Firefoksie wydaje się nieco szybsze niż użycie for...ofpętli. Różnica nie jest jednak tak ważna, a Chrome działa for...ofszybciej niż natywna forpętla, dlatego polecam korzystać for...ofz niej Object.valuesw każdym przypadku (4. i 6. test).

  • W przeglądarce Firefox for...inpętla jest bardzo wolna, więc jeśli chcemy buforować klucz podczas iteracji, lepiej jest go użyć Object.keys. Dodatkowo Chrome działa na obu strukturach z jednakową prędkością (pierwszy i ostatni test).

Możesz sprawdzić testy tutaj: https://jsperf.com/es7-and-misc-loops

Vdegenne
źródło
2
Przykład ES7 działa jak urok w React Native!
Ty Bailey,
Ładnie wyjaśnione. Dzięki
Alok Ranjan
30

Wiem, że jest późno, ale napisanie tej zoptymalizowanej i ulepszonej wersji odpowiedzi AgileJon zajęło mi 2 minuty:

var key, obj, prop, owns = Object.prototype.hasOwnProperty;

for (key in validation_messages ) {

    if (owns.call(validation_messages, key)) {

        obj = validation_messages[key];

        for (prop in obj ) {

            // using obj.hasOwnProperty might cause you headache if there is
            // obj.hasOwnProperty = function(){return false;}
            // but owns will always work 
            if (owns.call(obj, prop)) {
                console.log(prop, "=", obj[prop]);
            }

        }

    }

}
Azder
źródło
1
Czemu przechowywania hasOwnPropertyw owns, a następnie wywołanie owns.call(obj, prop)zamiast po prostu wywołanie obj.hasOwnProperty(prop)jak ta odpowiedź robi?
Rory O'Kane
14
Ponieważ objmoże mieć hasOwnPropertyzdefiniowaną funkcję na sobie, więc nie będzie korzystać z tej z Object.prototype. Możesz wypróbować przed taką forpętlą obj.hasOwnProperty = function(){return false;}i nie będzie iterować po żadnej właściwości.
Azder
4
@Azder +1 za odpowiedź i +1, gdybym mógł za fajną rzecz w Object.prototype.hasOwnProperty. Widziałem to wcześniej w kodzie źródłowym biblioteki podkreślenia, ale nie wiem dlaczego.
Samuel
29
for(var k in validation_messages) {
    var o = validation_messages[k];
    do_something_with(o.your_name);
    do_something_else_with(o.your_msg);
}
chaos
źródło
14

p jest wartością

for (var key in p) {
  alert(key + ' => ' + p[key]);
}

LUB

Object.keys(p).forEach(key => { console.log(key, p[key]) })
Wesam
źródło
9

W ES7 możesz wykonać:

for (const [key, value] of Object.entries(obj)) {
  //
}
Kévin Berthommier
źródło
Zrobiłem kilka testów, ta metoda jest naprawdę powolna w przypadku dużej ilości danych.
vdegenne
8
for(var key in validation_messages){
    for(var subkey in validation_messages[key]){
        //code here
        //subkey being value, key being 'yourname' / 'yourmsg'
    }
}
Dmitri Farkov
źródło
7

Niewiele sposobów na zrobienie tego ...

1) 2 warstwy dla ... w pętli ...

for (let key in validation_messages) {
   const vmKeys = validation_messages[key];
   for (let vmKey in vmKeys) {
      console.log(vmKey + vmKeys[vmKey]);
   }
}

2) KorzystanieObject.key

Object.keys(validation_messages).forEach(key => {
   const vmKeys = validation_messages[key];
   Object.keys(vmKeys).forEach(key => {
    console.log(vmKeys + vmKeys[key]);
   });
});

3) Funkcja rekurencyjna

const recursiveObj = obj => {
  for(let key in obj){
    if(!obj.hasOwnProperty(key)) continue;

    if(typeof obj[key] !== 'object'){
      console.log(key + obj[key]);
    } else {
      recursiveObj(obj[key]);
    }
  }
}

I nazwij to tak:

recursiveObj(validation_messages);
Alireza
źródło
5

Oto ulepszona i rekurencyjna wersja rozwiązania AgileJon ( demo ):

function loopThrough(obj){
  for(var key in obj){
    // skip loop if the property is from prototype
    if(!obj.hasOwnProperty(key)) continue;

    if(typeof obj[key] !== 'object'){
      //your code
      console.log(key+" = "+obj[key]);
    } else {
      loopThrough(obj[key]);
    }
  }
}
loopThrough(validation_messages);

To rozwiązanie działa na różnego rodzaju głębokościach.

JepZ
źródło
5

Inna opcja:

var testObj = {test: true, test1: false};
for(let x of Object.keys(testObj)){
    console.log(x);
}
koleś
źródło
Wypróbowałem twoje rozwiązanie w Chrome 55.0 i pojawia się błąd typu. Twoja odpowiedź wygląda ładnie i zwięźle, jeśli uda ci się ją uruchomić, prawdopodobnie będzie to jedna z lepszych opcji. Próbowałem to rozgryźć, ale nie rozumiem twojego rozwiązania.
TolMera
2
@TolMera Naprawiono.
koleś
4

ECMAScript-2017, właśnie ukończony miesiąc temu, wprowadza Object.values ​​(). Teraz możesz to zrobić:

let v;
for (v of Object.values(validation_messages))
   console.log(v.your_name);   // jimmy billy
Chong Lip Phang
źródło
3

Myślę, że warto zauważyć, że jQuery ładnie to rozwiązuje $.each().

Zobacz: https://api.jquery.com/each/

Na przykład:

$('.foo').each(function() {
    console.log($(this));
});

$(this)będąc pojedynczym przedmiotem wewnątrz obiektu. Zamień $('.foo')na zmienną, jeśli nie chcesz używać silnika wyboru jQuery.

Daniel Dewhurst
źródło
3

var obj={
name:"SanD",
age:"27"
}
Object.keys(obj).forEach((key)=>console.log(key,obj[key]));

Do przechodzenia przez obiekt JavaScript możemy użyć forEach, a do optymalizacji kodu możemy użyć funkcji strzałki

Sandip Bailkar
źródło
2

Nie mogłem nakłonić powyższych postów do zrobienia tego, o co mi chodziło.

Po przeanalizowaniu innych odpowiedzi tutaj zrobiłem to. Jest hacky, ale działa!

Dla tego obiektu:

var myObj = {
    pageURL    : "BLAH",
    emailBox   : {model:"emailAddress", selector:"#emailAddress"},
    passwordBox: {model:"password"    , selector:"#password"}
};

... ten kod:

// Get every value in the object into a separate array item ...
function buildArray(p_MainObj, p_Name) {
    var variableList = [];
    var thisVar = "";
    var thisYes = false;
    for (var key in p_MainObj) {
       thisVar = p_Name + "." + key;
       thisYes = false;
       if (p_MainObj.hasOwnProperty(key)) {
          var obj = p_MainObj[key];
          for (var prop in obj) {
            var myregex = /^[0-9]*$/;
            if (myregex.exec(prop) != prop) {
                thisYes = true;
                variableList.push({item:thisVar + "." + prop,value:obj[prop]});
            }
          }
          if ( ! thisYes )
            variableList.push({item:thisVar,value:obj});
       }
    }
    return variableList;
}

// Get the object items into a simple array ...
var objectItems = buildArray(myObj, "myObj");

// Now use them / test them etc... as you need to!
for (var x=0; x < objectItems.length; ++x) {
    console.log(objectItems[x].item + " = " + objectItems[x].value);
}

... tworzy to w konsoli:

myObj.pageURL = BLAH
myObj.emailBox.model = emailAddress
myObj.emailBox.selector = #emailAddress
myObj.passwordBox.model = password
myObj.passwordBox.selector = #password
użytkownik1833875
źródło
0

Rozwiązanie, które działa dla mnie, jest następujące

_private.convertParams=function(params){
    var params= [];
    Object.keys(values).forEach(function(key) {
        params.push({"id":key,"option":"Igual","value":params[key].id})
    });
    return params;
}
Jorge Santos Neill
źródło
0

Egzotyczny - głęboki trawers

JSON.stringify(validation_messages,(field,value)=>{
  if(!field) return value;

  // ... your code

  return value;
})

W tym rozwiązaniu wykorzystujemy zamiennik, który pozwala na głębokie przemierzanie całego obiektu i obiektów zagnieżdżonych - na każdym poziomie otrzymasz wszystkie pola i wartości. Jeśli potrzebujesz uzyskać pełną ścieżkę do każdego pola, spójrz tutaj

Kamil Kiełczewski
źródło
-6

W moim przypadku (na podstawie powyższego) możliwa jest dowolna liczba poziomów.

var myObj = {
    rrr: undefined,
    pageURL    : "BLAH",
    emailBox   : {model:"emailAddress", selector:"#emailAddress"},
    passwordBox: {model:"password"    , selector:"#password"},
    proba: {odin:{dva:"rr",trr:"tyuuu"}, od:{ff:5,ppa:{ooo:{lll:'lll'}},tyt:'12345'}}
};


function lookdeep(obj,p_Name,gg){
    var A=[], tem, wrem=[], dd=gg?wrem:A;
    for(var p in obj){
        var y1=gg?'':p_Name, y1=y1 + '.' + p;
        if(obj.hasOwnProperty(p)){
           var tem=obj[p];
           if(tem && typeof tem=='object'){
               a1=arguments.callee(tem,p_Name,true);
               if(a1 && typeof a1=='object'){for(i in a1){dd.push(y1 + a1[i])};}
            }
            else{
               dd.push(y1 + ':' + String(tem));
            }
        }
    };
    return dd
};


var s=lookdeep(myObj,'myObj',false);
for (var x=0; x < s.length; ++x) {
console.log(s[x]+'\n');}

wynik:

["myObj.rrr:undefined",
"myObj.pageURL:BLAH",
"myObj.emailBox.model:emailAddress",
"myObj.emailBox.selector:#emailAddress",
"myObj.passwordBox.model:password",
"myObj.passwordBox.selector:#password",
"myObj.proba.odin.dva:rr",
"myObj.proba.odin.trr:tyuuu",
"myObj.proba.od.ff:5",
"myObj.proba.od.ppa.ooo.lll:lll",
"myObj.proba.od.tyt:12345"]
użytkownik2515312
źródło