2

Below is an a function that contains of an object, and I want to be able to access a specific body within the object with the use of an argument, that is with an index.

The problem is that when I try to access a property this way I get

undefined

when console logs it. What am I doing wrong?

moveLeftBtn.on('click', function(nr){
    var theBody = {
        bodies: {
            a: 'foo1',
            b: 'foo2',
            c: 'foo3'
        }
    };

    var newBody = theBody.bodies[1];    // temporarily hardcoded value instead of argument
    console.log(newBody);    // <-- undefined, why?

    return newBody;
});

EDIT

If I console log theBody.bodies I can see it's value (Object {a: Array[5], b: Array[5], c: Array[5]}), but when I try to access it's properties with [1] I get

undefined

(even though the properties contains of strings).

Peyman Mohamadpour
  • 17,954
  • 24
  • 89
  • 100
holyredbeard
  • 19,619
  • 32
  • 105
  • 171
  • 4
    `theBody.bodies[nr]` is correct. This is called "bracket notation". See also: http://stackoverflow.com/questions/11922383/access-process-nested-objects-arrays-or-json. Your edited code works fine: http://jsfiddle.net/q9ECP/. Note that bracket notation is not a special notation for arrays, it's how property access works in general. **If** identifiers were allowed to start with a digit, you could access an array with `arr.1` instead of `arr[1]`. The same way you can access plain objects either with `obj.foo` or `obj['foo']`. – Felix Kling Jun 13 '13 at 19:39
  • 1
    Relevant section in the specification: http://es5.github.io/#x11.2.1. – Felix Kling Jun 13 '13 at 19:44
  • if you want the items to be `null`, remove quotes around `'null'` values – Igor Jun 13 '13 at 19:52
  • there is a superfluous comma after `'test3'` – Igor Jun 13 '13 at 19:54
  • @Igor: Yes, I know. Just put something in there but I changed it now. – holyredbeard Jun 13 '13 at 19:54
  • @Igor: Fixed the comma btw, thx. – holyredbeard Jun 13 '13 at 19:57
  • 1
    @holyredbeard - ok, now there is a missing semicolon after `theBody` assignment – Igor Jun 13 '13 at 19:57
  • 1
    If you see `(Object {a: Array[5], b: Array[5], c: Array[5]})`, then the properties of `theBody.bodies'` are `a`, `b`, `c`, not `1`, `2`, `3`. Of course `theBody.bodies[1]` will show `undefined` then. It should be `theBody.bodies['a']`. Do you want to access a property by index instead by name? That's not possible (at least not directly). – Felix Kling Jun 13 '13 at 19:58
  • The representation of your data in your question doesn't seem to match what you actually have. – Kevin B Jun 13 '13 at 19:59
  • So, what's your actual question, now that you changed the object? Do you really want to access the object with an index? Please clarify. – Felix Kling Jun 13 '13 at 20:01
  • @FelixKling: Convert your comment to an answer because you got the solution :) – holyredbeard Jun 13 '13 at 20:05
  • TBH, I'd rather close this as "too localized". – Felix Kling Jun 13 '13 at 20:07

4 Answers4

1

The only issue that I see with your code is that you aren't returning anything. Change . . .

var newBody = theBody.bodies[nr];

. . . to . . .

return theBody.bodies[nr];

. . . and you should be good to go.

Edit: Oh, you're also missing the ; after you define theBody.

talemyn
  • 7,822
  • 4
  • 31
  • 52
  • Thanks! Thing is that I get undefined if doing that. Very strange! – holyredbeard Jun 13 '13 at 19:51
  • What value are you passing in? I've tested it locally (after adding in the return statement, above) and calling the function like this: `AccessBody(1);` is returning the text in the object (tested in IE7 and Firefox). – talemyn Jun 13 '13 at 19:55
  • Also missing a colon after defining `theBody` . . . updated answer. – talemyn Jun 13 '13 at 20:00
  • *"Oh, you're also missing the `;` after you define `theBody`."* Luckily semicolons are optional ;) The problem is not with the syntax, it's with the data structure. – Felix Kling Jun 13 '13 at 20:00
  • I updated my questions to the exact function i'm trying with (now hardcoded value, [1], instead of argument). Same thing. :/ – holyredbeard Jun 13 '13 at 20:02
  • Yeah, what @FelixKling said (up in the question comments) . . . now that you've changed the keys to letters, you have to call them with the strings "a", "b", and "c". – talemyn Jun 13 '13 at 20:10
1

theBody.bodies is an object with three properties named a, b, and c. You did not give it any property named 1. That's why the value of theBody.bodies[1] is undefined. If you reference theBody.bodies['b'] you will find one of the properties you expect, with a value of 'foo1'.

It sounds like you just need to read up on JavaScript arrays and objects and how they work. Don't make any assumptions based on things like "associative arrays" you may have used in other languages. JavaScript is a language of its own with its own ways of doing things, so you just need to learn those.

Michael Geary
  • 28,450
  • 9
  • 65
  • 75
0

theBody.bodies is not an array but a plain object. Normally you cannot access fields of plain object with numeric indices.

But you can walkaround it by defining theBody.bodies object like that:

var theBody = {
    bodies: {
        '0': 'foo1',
        '1': 'foo2',
        '2': 'foo3'
    }
}

With bodies defined in such way there will be theBody.bodies[1] === 'foo2'.

If you by any reasons cannot change bodies than you may process it and create another object which fields you can access with numeric index - like that:

var newBodies = [], i = 0, j;
for (j in theBody.bodies) {
    newBodies.push(theBody.bodies[j]);
}

In case you theBody.bodies in not always plain object (for example instance of some type) than instead of for loop it should be iterated with $.each or for loop should be extended with hasOwnProperty check.

Dmitry
  • 101
  • 3
  • *"Normally you cannot access fields of plain object with numeric indices."* Well, it's no different for arrays (since arrays are just objects as well). You can only access properties that exist. The *property name* can be any arbitrary character sequence though. In case of arrays, numerical property names are treated in a special way. – Felix Kling Jun 13 '13 at 20:37
0

Indexing works great on Firefox, but I don's know about other browsers. You will have to add more logic for index checking. You should not access objects by an index anyway, because the order of the properties may change and you should treat them as a map or a set as opposed to an indexed list.

I attached a getByIndex() function on all objects for this example. You can create your own object and attach the function to it.

Here is a JSFiddle I made that works for what you need it to do: http://jsfiddle.net/zKrbr/4/

Object.prototype.getByIndex = function (property, index) {
    var n = 0;
    if (this.hasOwnProperty(property)) {
        for (var prop in this[property]) {
            if (n == index) {
                console.log('Found Match: '+prop+': '+this[property][prop]);
                return this[property][prop];
            } else {
                n++;
            }
        }
    }
    return undefined;
}

$('#moveLeft').on('click', function (nr) {
    var theBody = {
        bodies: {
            a: 'foo1',
            b: 'foo2',
            c: 'foo3'
        }
    };
    var index = parseInt($('#input').val());
    var currIndex = !isNaN(index) ? index : 0;
    var newBody = theBody.getByIndex('bodies', currIndex);
    $('#output').val(newBody);
});
<strong>Index:<strong>
<input type="text" id="input" size="3"/>
<input type="button" id="moveLeft" value="Move Left" />
<input type="text" id="output" />
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • Your `Object.prototype.getByIndex` function won't work reliably, because properties are not sorted. The order in which the properties are iterated over can be different from browser to browser. See http://stackoverflow.com/q/280713/218196. – Felix Kling Jun 13 '13 at 20:41
  • I understand, as I have stated: `you should treat them as a map or a set as opposed to an indexed list`. I was just throwing out a suggestion. – Mr. Polywhirl Jun 13 '13 at 20:44
  • Ok. I wouldn't suggest something like that though. It's just too unreliable. – Felix Kling Jun 13 '13 at 20:46