74

I'm having a really hard time trying to find a way to iterate through this object in the way that I'd like. I'm using only Javascript here.

First, here's the object

{
"dialog":
{
    "dialog_trunk_1":{
        "message": "This is just a JSON Test"
    },
    
    "dialog_trunk_2":{
        "message": "and a test of the second message"
    },

    "dialog_trunk_3":
    {
        "message": "This is a test of a bit longer text. Hopefully this will at the very least create 3 lines and trigger us to go on to another box. So we can test multi-box functionality, too."
    }
}
}

Right now, I'm just trying basic ways to get through to each dialog_trunk on this object. I ideally want to loop through the object and for each trunk, display it's message value.

I've tried using a for loop to generate the name/number of the dialog_trunk on the fly, but I can't access the object using a string for the object name so I'm not sure where to go from here.

Eric
  • 2,201
  • 5
  • 29
  • 35
  • You need a for..in loop – bfavaretto Oct 11 '13 at 17:13
  • 1
    possible duplicate of [Access / process (nested) objects, arrays or JSON](http://stackoverflow.com/questions/11922383/access-process-nested-objects-arrays-or-json) – bfavaretto Oct 11 '13 at 17:13
  • 1
    If possible, you should change your structure to use an ordered list. There's little sense in using a named key with an incrementing index. `{"dialog":{"trunks":[{"message":"..."},{"message":"..."}]}}` – user2736012 Oct 11 '13 at 17:17
  • If it's JSON, have you parsed it? – Andy Oct 11 '13 at 17:17
  • @dcp You're so useful, thank you. Perhaps next time I'll try to include that I couldn't find something similar enough to my own structure that I could figure out the proper way to go through. It's a shame that has to be explicitly stated these days. – Eric Oct 11 '13 at 17:18
  • @Andy, I have parsed it, yes. I should have mentioned that. I'm having trouble handling it AFTER the parse. – Eric Oct 11 '13 at 17:18
  • user2736012 you've got a good point. A structure like that would be a lot easier to iterate through. I'll try something like that along with Roel's answer – Eric Oct 11 '13 at 17:20
  • @Eric: If you use that structure, be sure to not use `for-in` on the Array. – user2736012 Oct 11 '13 at 17:21
  • I actually ended up not having to use a for..in loop. Just using a simpler structure and putting the ID of the branch inside each object, it made everything so much simpler. This was just a matter of bad planning. – Eric Oct 11 '13 at 17:30

6 Answers6

168

You use a for..in loop for this. Be sure to check if the object owns the properties or all inherited properties are shown as well. An example is like this:

var obj = {a: 1, b: 2};
for (var key in obj) {
  if (obj.hasOwnProperty(key)) {
    var val = obj[key];
    console.log(val);
  }
}

Or if you need recursion to walk through all the properties:

var obj = {a: 1, b: 2, c: {a: 1, b: 2}};
function walk(obj) {
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      var val = obj[key];
      console.log(val);
      walk(val);
    }
  }
}
walk(obj);
Deathspike
  • 8,582
  • 6
  • 44
  • 82
  • 4
    `for..in` is in the ECMAScript 1.0 specification. Even Internet Explorer 5 supports it.. (Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in) – Deathspike Mar 27 '14 at 22:47
14

My problem was actually a problem of bad planning with the JSON object rather than an actual logic issue. What I ended up doing was organize the object as follows, per a suggestion from user2736012.

{
"dialog":
{
    "trunks":[
    {
        "trunk_id" : "1",
        "message": "This is just a JSON Test"
    },
    {
        "trunk_id" : "2",
        "message": "This is a test of a bit longer text. Hopefully this will at the very least create 3 lines and trigger us to go on to another box. So we can test multi-box functionality, too."
    }
    ]
}
}

At that point, I was able to do a fairly simple for loop based on the total number of objects.

var totalMessages = Object.keys(messages.dialog.trunks).length;

    for ( var i = 0; i < totalMessages; i++)
    {
        console.log("ID: " + messages.dialog.trunks[i].trunk_id + " Message " + messages.dialog.trunks[i].message);
    }

My method for getting totalMessages is not supported in all browsers, though. For my project, it actually doesn't matter, but beware of that if you choose to use something similar to this.

Eric
  • 2,201
  • 5
  • 29
  • 35
8

Here is my recursive approach:

function visit(object) {
    if (isIterable(object)) {
        forEachIn(object, function (accessor, child) {
            visit(child);
        });
    }
    else {
        var value = object;
        console.log(value);
    }
}

function forEachIn(iterable, functionRef) {
    for (var accessor in iterable) {
        functionRef(accessor, iterable[accessor]);
    }
}

function isIterable(element) {
    return isArray(element) || isObject(element);
}

function isArray(element) {
    return element.constructor == Array;
}

function isObject(element) {
    return element.constructor == Object;
}
schirrmacher
  • 2,341
  • 2
  • 27
  • 29
  • this is nicely done! works verry well! How to add the key as string in front of the value like: key:value? – novski Feb 17 '19 at 12:22
0

An improved version for recursive approach suggested by @schirrmacher to print key[value] for the entire object:

var jDepthLvl = 0;
function visit(object, objectAccessor=null) {
  jDepthLvl++;
  if (isIterable(object)) {
    if(objectAccessor === null) {
      console.log("%c ⇓ ⇓ printing object $OBJECT_OR_ARRAY$ -- START ⇓ ⇓", "background:yellow");
    } else
      console.log("%c"+spacesDepth(jDepthLvl)+objectAccessor+"%c:","color:purple;font-weight:bold", "color:black");
    forEachIn(object, function (accessor, child) {
      visit(child, accessor);
    });
  } else {
    var value = object;
    console.log("%c"
      + spacesDepth(jDepthLvl)
      + objectAccessor + "[%c" + value + "%c] "
      ,"color:blue","color:red","color:blue");
  }
  if(objectAccessor === null) {
    console.log("%c ⇑ ⇑ printing object $OBJECT_OR_ARRAY$ -- END ⇑ ⇑", "background:yellow");
  }
  jDepthLvl--;
}

function spacesDepth(jDepthLvl) {
  let jSpc="";
  for (let jIter=0; jIter<jDepthLvl-1; jIter++) {
    jSpc+="\u0020\u0020"
  }
  return jSpc;
}

function forEachIn(iterable, functionRef) {
  for (var accessor in iterable) {
    functionRef(accessor, iterable[accessor]);
  }
}

function isIterable(element) {
  return isArray(element) || isObject(element);
}

function isArray(element) {
  return element.constructor == Array;
}

function isObject(element) {
  return element.constructor == Object;
}


visit($OBJECT_OR_ARRAY$);

Console Output using JSON from @eric

JMerinoH
  • 88
  • 8
0

var res = {
              "dialog":
              {
                  "dialog_trunk_1":{
                      "message": "This is just a JSON Test"
                  },

                  "dialog_trunk_2":{
                      "message": "and a test of the second message"
                  },

                  "dialog_trunk_3":
                  {
                      "message": "This is a test of a bit longer text. Hopefully this will at the very least create 3 lines and trigger us to go on to another box. So we can test multi-box functionality, too."
                  }
              }
          }


for (var key in res) {
  if (res.hasOwnProperty(key)) {
    var val = res[key];
    for (var key in val) {
      if (val.hasOwnProperty(key)) {
        var dialog = val[key];
        console.log(dialog.message);
      }
    }
  }
}
Codemaker2015
  • 12,190
  • 6
  • 97
  • 81
0

The simpler approach is (just found on W3Schools):

let data = {.....}; // JSON Object
for(let d in data){
    console.log(d); // It gives you property name
    console.log(data[d]); // And this gives you its value
}

UPDATE

This approach works fine until you deal with the nested object so this approach will work.

const iterateJSON = (jsonObject, output = {}) => {
  for (let d in jsonObject) {
    if (typeof jsonObject[d] === "string") {
      output[d] = jsonObject[d];
    }
    if (typeof jsonObject[d] === "object") {
      output[d] = iterateJSON(jsonObject[d]);
    }
  }
  return output;
}

And use the method like this

let output = iterateJSON(your_json_object);
Usama Majid
  • 1,103
  • 13
  • 13