JavaScript中的多维关联数组

92

以下是查询结果:(key1和key2可以是任何文本)

id   key1     key2     value

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

我希望将所有记录像这样循环一遍,并将数据存储在一个网格中(可能是数组):

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

在 PHP 中,这将非常容易,使用关联数组:

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

但是在JavaScript中,像这样的关联数组不起作用。 经过阅读大量教程后,我得到的唯一结论就是:

arr=[];
arr[1]['apple'] = 2;
但是arr ['fred'] ['apple'] = 2;不起作用。我尝试了对象数组,但是对象属性不能是自由文本。我读了越多的教程,就越感到困惑...任何想法都受欢迎 :)

感谢回复,但我正在循环查询结果,并且希望逐个设置值。 例子中的行(取自Matt的例子)var grid = {};grid ['aa'] ['bb'] = 1;返回“Uncaught TypeError:无法设置未定义的属性'bb'”。 我可能错了,但是对于你们大多数示例,我必须在初始化时知道数据。 - Omiod
刚刚发现 var grid = {}; grid['aa'] = {}; grid['aa']['bb'] = 1; 可以运行。一个更复杂的测试失败了,但看起来我走在正确的道路上。 - Omiod
2
你必须先初始化子对象,就像我在答案中提到的那样。var grid = {}; grd['aa'] = {}; 然后你可以执行grid['aa']['bb'] = 1。有许多方法可以检查子对象是否已经初始化(如我在答案中提到的),这样你就不会覆盖现有的对象。 - Matt
我更新了我的答案,并添加了一些额外的代码。不确定你的对象有多深,或者你是如何获取数据的,但希望能指引你朝着正确的方向前进。 - Matt
10个回答

172

只需使用普通的JavaScript对象,这样它会“读取”与您的关联数组相同。您还必须记得首先进行初始化。

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

如果你正在查看数值,你可能会看到像这样的东西:

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

成功地将示例应用于生产代码(一个Chrome扩展程序),并且运行良好。谢谢。终于我明白如何在JS中处理对象了! - Omiod
3
无法赞同这个答案的次数太多了!导致我的对象一直返回未定义的部分是我没有初始化子对象。因此,请确保这样做!例如:grid[name] = {}; - Jason Pudzianowski
1
@Matt 有没有办法只使用一组 [] 就能获取 obj.fred.apples?我猜不行。例如 obj["fred.apples"] - theB3RV
如果我们不知道数组的数量,例如您的代码 [javasript] var people = ['fred', 'alice']; var fruit = ['apples', 'lemons']; var color = ['red', 'blue'] var..... --> 数组的数量未知 [/javascript] - YukiSakura
我可以通过数字索引访问对象(fred)中的第一项吗?就像obj [0]一样,它会返回apples:1,oranges:2。我可以访问最后一项吗(即alice并返回lemons:1)? - Timo
如果(typeof grid[name] == 'undefined'){ grid[name] = {}; } @Jason_Pudzianowski 这是您可以处理未定义对象的方式,以便您不会得到未定义的返回值。 - Rupesh Terase

32

如果不一定需要一个数组的话,你可以创建一个“多维”的JS对象...

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

JSON实际上是JavaScript对象的“字符串化”格式。你手头的不是JSON,只是一个JavaScript对象。 - Matt
1
@charliegriefer,文档写入行不应该是:"document.write( myObj ......"吗? - john

14

Javascript很灵活:

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

8
我认为这里的变量名"arr"是错误的,因为它实际上不是一个数组,而是一个对象。 - Matt
我是一只懒狗,所以无法使用你的解决方案。如果我很积极的话,我会通过你的子数组示例找到方法的;) 无论如何,感谢你的努力。 - Timo

9

在我需要以一种良好的方式获取所有元素时,我遇到了这个SO主题“穿越二维关联数组/对象” - 对我来说名称并不重要,因为功能才是最重要的。

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

应该被接受为答案,因为循环通常是解决问题的方法。 - Timo

7

对于一些应用程序而言,似乎在JavaScript中实现多维关联数组的方法更为简单。

  1. 由于所有数组的内部表示实际上是对象的对象,因此已经证明,数值索引元素的访问时间与关联(文本)索引元素的访问时间相同。

  2. 首级关联索引元素的访问时间不会随着实际元素数量的增加而增加。

鉴于此,在许多情况下,使用连接字符串方法创建等效的多维元素实际上可能更好。例如:

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

前往:

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

优点包括:
  • 无需初始化子对象或弄清如何最好地组合对象
  • 单层访问时间。 对象内的对象需要 N 倍的访问时间
  • 可以使用 Object.keys() 提取所有维度信息和 ...
  • 可以使用函数 regex.test(string) 和数组映射函数来提取您想要的内容。
  • 维度中没有层次结构。
  • "dot" 是任意的 - 使用下划线实际上使正则表达式更容易
  • 还有很多脚本可以将 JSON "平坦化" 成这种格式,并从中导出
  • 可以在 keylist 上使用所有其他好用的数组处理函数

4

你不一定需要使用对象,可以使用普通的多维数组来实现。

这是我没有使用对象的解决方案:

// Javascript
const matrix = [];

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

matrix.key2 = [
  'value3',
];

在 PHP 中等同于:

// PHP
$matrix = [
    "key1" => [
        'value1',
        'value2',
    ],
    "key2" => [
        'value3',
    ]
];

1
你之后怎么循环(使用每个)它? - Sagive

3

不要使用数组,使用对象。

var foo = new Object();

2
不要使用new Object(),因为Object.prototype可能会附加一些奇怪的东西;请使用对象字面量:var foo = {}; - Michael Paulukonis
2
@Michael 我觉得 {} 只是 new Object() 的语法糖,就像 [] 是 new Array() 的简写一样。 - Matt
1
从我的Chrome JS控制台来看,这两个构造函数看起来完全相同,包括它们的原型。甚至在两者中添加Object.prototype.foo = function(){}也是一样的。 - Matt
1
@Matt 我想我错了,我把那个和 new Array() 混淆了,后者应该避免使用。唉。 - Michael Paulukonis
2
@Michael 为什么应该避免使用 new Array()? - Matt
1
@Matt,请参考http://www.jslint.com/lint.html中的“构造函数和new”以及https://dev59.com/MHRC5IYBdhLWcg3wMd9S,其中很多人认为它是无用的。所以需要谨慎对待。 - Michael Paulukonis

3

当属性名为整数时,获取关联数组的属性值:

从一个属性名为整数的关联数组开始:

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

向数组中添加元素:

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

循环遍历数组并对属性值执行某些操作。

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

控制台输出应该如下所示:

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

正如你所看到的,你可以绕过关联数组的限制,将属性名设为整数。

注意:我示例中的关联数组是指序列化Dictionary[]对象后的json。


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>


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

输出:

  • 找到基础
  • 找到Base.panel.panel_base
  • 旧值:Base
  • 新值:新值

数组操作正常,没有问题。

迭代:

     Object.keys(myObj['Base']).forEach(function(key, index) {            
        var value = objcons['Base'][key];                   
      }, myObj);

2
欢迎来到stackoverflow。请编辑您的答案并解释为什么您的代码片段可以解决问题。更多信息请参见:[answer] - Djensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接