92

There is the following query results: (key1 and key2 could be any text)

id   key1     key2     value

1    fred     apple    2
2    mary     orange   10
3    fred     banana   7
4    fred     orange   4
5    sarah    melon    5
...

and I wish to store the data in a grid (maybe as an array) looping all the records like this:

         apple    orange   banana  melon
fred        2        4         7     -
mary        -        10        -     -
sarah       -        -         -     5

In PHP this would be really easy, using associative arrays:

$result['fred']['apple'] = 2;

But in JavaScript associative arrays like this doesn't work. After reading tons of tutorial, all I could get was this:

arr=[];
arr[1]['apple'] = 2;

but arr['fred']['apple'] = 2; doesn't work. I tried arrays of objects, but objects properties can't be free text. The more I was reading tutorials, the more I got confused...

Any idea is welcome :)

user2357112
  • 260,549
  • 28
  • 431
  • 505
Omiod
  • 11,285
  • 11
  • 53
  • 59
  • Thanks for the replies, but I'm looping through the query results, and I wish to set the values one at a time. The example lines (taken form Matt example) `var grid = {};grid['aa']['bb'] = 1;` returns "Uncaught TypeError: Cannot set property 'bb' of undefined". I could be wrong, but with most of your examples I have to know the data at initialization time. – Omiod Dec 01 '10 at 21:31
  • Just found that `var grid = {}; grid['aa'] = {}; grid['aa']['bb'] = 1;` works. A more complex test fails, but looks like I'm in the right path – Omiod Dec 01 '10 at 21:45
  • 2
    you have to initialize the sub-object first, as I mentioned in my answer. var grid = {}; grd['aa'] = {}; *then* you can do grid['aa']['bb'] = 1. There are many ways to check to see if the sub-object is already initialized (as mentioned in my answer), so you don't overwrite an existing object. – Matt Dec 01 '10 at 21:45
  • updated my answer with some additional code. not sure how deep your objects are, or how you're getting your data, but hopefully will point you in the right direction – Matt Dec 01 '10 at 21:52

10 Answers10

172

Just use a regular JavaScript object, which would 'read' the same way as your associative arrays. You have to remember to initialize them first as well.

var obj = {};

obj['fred'] = {};
if('fred' in obj ){ } // can check for the presence of 'fred'
if(obj.fred) { } // also checks for presence of 'fred'
if(obj['fred']) { } // also checks for presence of 'fred'

// The following statements would all work
obj['fred']['apples'] = 1;
obj.fred.apples = 1;
obj['fred'].apples = 1;

// or build or initialize the structure outright
var obj = { fred: { apples: 1, oranges: 2 }, alice: { lemons: 1 } };

If you're looking over values, you might have something that looks like this:

var people = ['fred', 'alice'];
var fruit = ['apples', 'lemons'];

var grid = {};
for(var i = 0; i < people.length; i++){
    var name = people[i];
    if(name in grid == false){
        grid[name] = {}; // must initialize the sub-object, otherwise will get 'undefined' errors
    }

    for(var j = 0; j < fruit.length; j++){
        var fruitName = fruit[j];
        grid[name][fruitName] = 0;
    }
}
Matt
  • 41,216
  • 30
  • 109
  • 147
  • Managed to use the example on production code (a Chrome extension), and works fine. Thanks. Finally I'm getting how to handle object in JS! – Omiod Dec 01 '10 at 22:04
  • 3
    Can't upvote this answer enough! The part that was causing my object to continually return undefined was that I was NOT initializing the sub-object. **So make sure to to do this!** _example:_ `grid[name] = {};` – Jason Pudzianowski Jan 12 '12 at 10:01
  • 1
    @Matt Is there a way to get obj.fred.apples using only one set of []? I'm assuming not. `obj["fred.apples"]` for example – theB3RV Oct 19 '17 at 16:06
  • What if we don't know the number of array, for example your code [javasript] var people = ['fred', 'alice']; var fruit = ['apples', 'lemons']; var color = ['red', 'blue'] var..... --> the number of array is unknown [/javascript] – YukiSakura Nov 29 '17 at 17:32
  • Can I access the first item in obj (fred) by numeric index? Like obj[0] which results in apples:1, oranges:2 . Can I access the last item (which is alice and results in lemons:1)?` – Timo Aug 11 '20 at 12:01
  • if(typeof grid[name] == 'undefined'){ grid[name] = {}; } @Jason_Pudzianowski this is how u can handle undefined object so that u dont get undefinded in return – Rupesh Terase Apr 27 '21 at 12:15
32

If it doesn't have to be an array, you can create a "multidimensional" JS object...

<script type="text/javascript">
var myObj = { 
    fred: { apples: 2, oranges: 4, bananas: 7, melons: 0 }, 
    mary: { apples: 0, oranges: 10, bananas: 0, melons: 0 }, 
    sarah: { apples: 0, oranges: 0, bananas: 0, melons: 5 } 
}

document.write(myObj['fred']['apples']);
</script>
Techie Boy
  • 139
  • 1
  • 12
charliegriefer
  • 3,342
  • 1
  • 18
  • 20
  • JSON is actually the 'stringified' format of the JavaScript object. What you have there isn't JSON, just a JavaScript object. – Matt Dec 01 '10 at 21:10
  • 1
    @charliegriefer, shouldn't the document.write line be: "document.write( myObj ......" – john Jan 04 '15 at 16:56
14

Javascript is flexible:

var arr = {
  "fred": {"apple": 2, "orange": 4},
  "mary": {}
  //etc, etc
};

alert(arr.fred.orange);
alert(arr["fred"]["orange"]);
for (key in arr.fred)
    alert(key + ": " + arr.fred[key]);
cambraca
  • 27,014
  • 16
  • 68
  • 99
  • 8
    I would say the variable name 'arr' is a misnomer here, because it isn't actually an array, but an object. – Matt Dec 01 '10 at 21:07
  • I am a lazy dog and therefore cannot use your solution. If I would have been eager, I would find my way with your subarray example;) Anyway, thanks for your effort. – Timo Jul 01 '20 at 14:31
9

As I needed get all elements in a nice way I encountered this SO subject "Traversing 2 dimensional associative array/object" - no matter the name for me, because functionality counts.

var imgs_pl = { 
    'offer':        { 'img': 'wer-handwritter_03.png', 'left': 1, 'top': 2 },
    'portfolio':    { 'img': 'wer-handwritter_10.png', 'left': 1, 'top': 2 },
    'special':      { 'img': 'wer-handwritter_15.png', 'left': 1, 'top': 2 }
};
for (key in imgs_pl) { 
    console.log(key);
    for (subkey in imgs_pl[key]) { 
        console.log(imgs_pl[key][subkey]);
    }
}
Marco Panichi
  • 1,068
  • 1
  • 18
  • 31
7

It appears that for some applications, there is a far simpler approach to multi dimensional associative arrays in javascript.

  1. Given that the internal representation of all arrays are actually as objects of objects, it has been shown that the access time for numerically indexed elements is actually the same as for associative (text) indexed elements.

  2. the access time for first-level associative indexed elements does not rise as the number of actual elements increases.

Given this, there may be many cases where it is actually better to use a concatenated string approach to create the equivalence of a multidimensional elements. For example:

store['fruit']['apples']['granny']['price'] = 10
store['cereal']['natural']['oats']['quack'] = 20

goes to:

store['fruit.apples.granny.price'] = 10
store['cereal.natural.oats.quack'] = 20

Advantages include:

  • no need to initialize sub-objects or figure out how to best combine objects
  • single-level access time. objects within objects need N times the access time
  • can use Object.keys() to extract all dimension information and..
  • can use the function regex.test(string) and the array.map function on the keys to pull out exactly what you want.
  • no hierarchy in the dimensions.
  • the "dot" is arbitrary - using underscore actually makes regex easier
  • there are lots of scripts for "flattening" JSON into and out of this format as well
  • can use all of the other nice array processing functions on keylist
MetaEd
  • 3,753
  • 1
  • 28
  • 30
sdw
  • 623
  • 9
  • 10
4

You don't need to necessarily use Objects, you can do it with normal multi-dimensional Arrays.

This is my solution without Objects:

// Javascript
const matrix = [];

matrix.key1 = [
  'value1',
  'value2',
];

matrix.key2 = [
  'value3',
];

which in PHP is equivalent to:

// PHP
$matrix = [
    "key1" => [
        'value1',
        'value2',
    ],
    "key2" => [
        'value3',
    ]
];
Francesco Borzi
  • 56,083
  • 47
  • 179
  • 252
3

Get the value for an array of associative arrays's property when the property name is an integer:

Starting with an Associative Array where the property names are integers:

var categories = [
    {"1":"Category 1"},
    {"2":"Category 2"},
    {"3":"Category 3"},
    {"4":"Category 4"}
];

Push items to the array:

categories.push({"2300": "Category 2300"});
categories.push({"2301": "Category 2301"});

Loop through array and do something with the property value.

for (var i = 0; i < categories.length; i++) {
    for (var categoryid in categories[i]) {
        var category = categories[i][categoryid];
        // log progress to the console
        console.log(categoryid + " : " + category);
        //  ... do something
    }
}

Console output should look like this:

1 : Category 1
2 : Category 2
3 : Category 3
4 : Category 4
2300 : Category 2300
2301 : Category 2301

As you can see, you can get around the associative array limitation and have a property name be an integer.

NOTE: The associative array in my example is the json you would have if you serialized a Dictionary[] object.

Jason Williams
  • 2,740
  • 28
  • 36
3

Don't use an array, use an object.

var foo = new Object();
Neuron
  • 5,141
  • 5
  • 38
  • 59
Tim
  • 8,669
  • 31
  • 105
  • 183
  • 2
    don't use a `new Object()`, since the Object.prototype could have weirdness attached to it; use the object literal: `var foo = {};` – Michael Paulukonis Dec 01 '10 at 21:19
  • 2
    @Michael I thought {} was just syntactic sugar for new Object(); Much like [] is short for new Array() – Matt Dec 01 '10 at 21:22
  • 1
    from my Chrome JS console, both of these constrcuts appear identical, including their prototypes. Even adding Object.prototype.foo = function(){} appears in both. – Matt Dec 01 '10 at 21:34
  • 1
    @Matt I think I'm wrong I was mixing that up with `new Array()` which should be avoided. ugh. – Michael Paulukonis Dec 01 '10 at 21:56
  • 2
    @Michael why should new Array() be avoided? – Matt Dec 01 '10 at 21:57
  • 1
    @Matt see "Constructors and new" in http://www.jslint.com/lint.html and also http://stackoverflow.com/questions/383402/is-javascript-s-new-keyword-considered-harmful wherein a lot of people say it's hooey. so grain-o-salt time. – Michael Paulukonis Dec 01 '10 at 22:02
0

<script language="javascript">

// Set values to variable
var sectionName = "TestSection";
var fileMap = "fileMapData";
var fileId = "foobar";
var fileValue= "foobar.png";
var fileId2 = "barfoo";
var fileValue2= "barfoo.jpg";

// Create top-level image object
var images = {};

// Create second-level object in images object with
// the name of sectionName value
images[sectionName] = {};

// Create a third level object
var fileMapObj = {};

// Add the third level object to the second level object
images[sectionName][fileMap] = fileMapObj;

// Add forth level associate array key and value data
images[sectionName][fileMap][fileId] = fileValue;
images[sectionName][fileMap][fileId2] = fileValue2;


// All variables
alert ("Example 1 Value: " + images[sectionName][fileMap][fileId]);

// All keys with dots
alert ("Example 2 Value: " + images.TestSection.fileMapData.foobar);

// Mixed with a different final key
alert ("Example 3 Value: " + images[sectionName]['fileMapData'][fileId2]);

// Mixed brackets and dots...
alert ("Example 4 Value: " + images[sectionName]['fileMapData'].barfoo);

// This will FAIL! variable names must be in brackets!
alert ("Example 5 Value: " + images[sectionName]['fileMapData'].fileId2);
// Produces: "Example 5 Value: undefined".

// This will NOT work either. Values must be quoted in brackets.
alert ("Example 6 Value: " + images[sectionName][fileMapData].barfoo);
// Throws and exception and stops execution with error: fileMapData is not defined

// We never get here because of the uncaught exception above...
alert ("The End!");
</script>
Rich Schramm
  • 141
  • 1
  • 8
0
    var myObj = [];
    myObj['Base'] = [];
    myObj['Base']['Base.panel.panel_base'] = {ContextParent:'',ClassParent:'',NameParent:'',Context:'Base',Class:'panel',Name:'panel_base',Visible:'',ValueIst:'',ValueSoll:'',
                                              Align:'',  AlignFrom:'',AlignTo:'',Content:'',onClick:'',Style:'',content_ger_sie:'',content_ger_du:'',content_eng:'' };
    myObj['Base']['Base.panel.panel_top']  = {ContextParent:'',ClassParent:'',NameParent:'',Context:'Base',Class:'panel',Name:'panel_base',Visible:'',ValueIst:'',ValueSoll:'',
                                              Align:'',AlignFrom:'',AlignTo:'',Content:'',onClick:'',Style:'',content_ger_sie:'',content_ger_du:'',content_eng:'' };

    myObj['SC1'] = [];
    myObj['SC1']['Base.panel.panel_base'] = {ContextParent:'',ClassParent:'',NameParent:'',Context:'Base',Class:'panel',Name:'panel_base',Visible:'',ValueIst:'',ValueSoll:'',
                                              Align:'',  AlignFrom:'',AlignTo:'',Content:'',onClick:'',Style:'',content_ger_sie:'',content_ger_du:'',content_eng:'' };
    myObj['SC1']['Base.panel.panel_top']  = {ContextParent:'',ClassParent:'',NameParent:'',Context:'Base',Class:'panel',Name:'panel_base',Visible:'',ValueIst:'',ValueSoll:'',
                                              Align:'',AlignFrom:'',AlignTo:'',Content:'',onClick:'',Style:'',content_ger_sie:'',content_ger_du:'',content_eng:'' };


    console.log(myObj);

    if ('Base' in myObj) {
      console.log('Base found');

      if ('Base.panel.panel_base' in myObj['Base'])  {
        console.log('Base.panel.panel_base found'); 


      console.log('old value: ' + myObj['Base']['Base.panel.panel_base'].Context);  
      myObj['Base']['Base.panel.panel_base'] = 'new Value';
      console.log('new value: ' + myObj['Base']['Base.panel.panel_base']);
      }
    }

Output:

  • Base found
  • Base.panel.panel_base found
  • old value: Base
  • new value: new Value

The array operation works. There is no problem.

Iteration:

     Object.keys(myObj['Base']).forEach(function(key, index) {            
        var value = objcons['Base'][key];                   
      }, myObj);
Necips
  • 11
  • 2
  • 2
    Welcome to stackoverflow. Please edit your answer and explain why your code snippets solves the problem. More info here: [answer] – Djensen Mar 22 '19 at 08:31