如何迭代对象的属性值对?

102

我有这样一个结构:

var myMap = {
    partnr1: ['modelA', 'modelB', 'modelC'],
    partnr2: ['modelA', 'modelB', 'modelC']
};

我将遍历每个元素(partnr)及其关联的模型。

我尝试使用双重$each()迭代来实现这一点,但是没有任何反应:

$.each(myMap, function (i, val) {
    $.each(i, function (innerKey, innerValue) {

        setTimeout(function () {
            $('#variant').fadeOut("slow", function () {
                $(this).text(innerKey + "-" + innerValue).fadeIn("slow");

            });

        }, i * 6000);

    });
});

当我使用单值数组 (Object) 时,我尝试达到的淡入淡出效果可以正常工作,但是当我需要每个键具有多个值时,就不能正常工作,比如这里。

如何成功完成迭代?除了使用Object之外,在这种情况下是否有更好的方式?

8个回答

168

2019年您的问题的答案:

这取决于您使用的ECMAScript版本。

ES6之前:

使用下面任何一个答案,例如:

for (var m in myMap){
    for (var i=0;i<myMap[m].length;i++){
    ... do something with myMap[m][i] ...
    }
} 

对于ES6(ES 2015):

您应该使用一个具有entries()函数的Map对象:

var myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");

for (const [key, value] of myMap.entries()) {
  console.log(key, value);
}

对于ES8(ES 2017):

Object.entries()被引入:

const object = {'a': 1, 'b': 2, 'c' : 3};
for (const [key, value] of Object.entries(object)) {
  console.log(key, value);
}

2
我想问一个愚蠢的问题,为什么要使用ES8,当最终用户可能使用旧版浏览器? - Erik
11
@Erik:JS开发并不仅限于Web开发领域,除此之外还有其他领域。在这些领域中,您可能无需担心旧版浏览器的问题,可以享受ES8带来的改进。 - David Schumann
3
@Erik,像 Babel 这样的转译器有助于将新版本的 JS 转换为旧版本的JS。因此,即使用户使用旧版浏览器,我们经过转译的 ES8 代码仍然能在旧版浏览器上运行。 - kaushalpranav
经过最新的编辑(2021年12月10日),第三种方法是ES6代码片段的重复。使用Object.entries的先前版本是正确的,因为它展示了如何迭代用作映射的原始JavaScript对象,而不是ES6 Map类型。 - Tristan MacKinlay
1
感谢 @TristanMacKinlay。我撤销了这个编辑。 - David Schumann
显示剩余2条评论

48

我会使用标准的JavaScript:

for (var m in myMap){
    for (var i=0;i<myMap[m].length;i++){
    ... do something with myMap[m][i] ...
    }
} 

注意处理对象和数组的不同方式。


1
为什么?而且你忘了必要的闭包... - Bergi
这样做更快,而且在我看来更清晰。我跳过了剩下的部分,因为我不清楚它应该做什么。#variant是什么?#variant最终应该做什么? - Atle
#variant 是一个 div 的 id,将使用地图中的信息显示(淡入)。对于每个 partnr,应该淡入一个包含相应型号的列表。 - user2374903

42

ES6+的函数式方法

如果你想对Map对象进行更加函数式的迭代,你可以像这样操作

const myMap = new Map() 
myMap.forEach((value, key) => {
    console.log(value, key)
})

7

看起来这个旧的JQuery线程被ES6 Map用户所占领。

如果这是你想要的,我建议使用Array.from()函数将Map转换为Array。这使得你可以轻松地链接变换操作,如filter()map()等。

const map = new Map([
  ['key one', 'value one'],
  ['key two', 'value two'],
]);

// Loop through key-value-pairs
Array.from(map.entries()).map(([key, val]) => console.log(key, val));

// Loop through map keys
Array.from(map.keys()).map(key => console.log(key));

// Loop through values
Array.from(map.values()).map(value => console.log(value));

5

$.each()方法的回调函数将按照顺序传递属性名和属性值。因此,在内部调用$.each()时,您正在尝试迭代属性名称。我认为您需要使用以下方式:

$.each(myMap, function (i, val) {
  $.each(val, function(innerKey, innerValue) {
    // ...
  });
});

在内部循环中,对于像您的地图一样的对象,值是数组。这没问题,但请注意,“innerKey”值都将是数字。
编辑 - 现在解决了那个问题,接下来是下一个问题:
    setTimeout(function () {

      // ...

    }, i * 6000);

第一次循环时,“i”将是字符串“partnr1”。 因此,乘法尝试将导致NaN。 您可以保持外部计数器以跟踪外部映射的属性计数:
var pcount = 1;
$.each(myMap, function(i, val) {
  $.each(val, function(innerKey, innerValue) {
    setTimeout(function() {
      // ...
    }, pcount++ * 6000);
  });
});

谢谢您的回复。通过将$.each(i...)更改为$.each(val..),我上面的代码正在以一种显示结果的方式工作。但不幸的是,它只显示映射中的最后一个结果,因此它直接跳到最终迭代。 - user2374903
@user2374903 不,它并没有跳到最后一个,尽管看起来是这样。我没有仔细看“setTimeout”调用,所以我会在我的答案中添加。 - Pointy
通过将pcount从1更改为0(这意味着它从第一个循环开始),代码可以工作,但只能逐个显示模型。我的错误是没有明确说明,目标是将与该部件号对应的所有型号作为一个列表显示出来。 - user2374903

1
不要使用迭代器来完成此操作。在回调函数中通过增加计数器并递归调用下一个项目来维护自己的循环。
$.each(myMap, function(_, arr) {
    processArray(arr, 0);
});

function processArray(arr, i) {
    if (i >= arr.length) return;

    setTimeout(function () {
        $('#variant').fadeOut("slow", function () {
            $(this).text(i + "-" + arr[i]).fadeIn("slow");

            // Handle next iteration
            processArray(arr, ++i);
        });
    }, 6000);
}

虽然你的代码存在逻辑错误。你正在将同一个容器设置为多个不同的值,几乎是在同一时间。也许你的意思是让每个容器更新自己的内容。


谢谢您的建议。我尝试了您的代码,但您说得对,容器存在问题。下面的代码可以工作,但这里只有一个单值数组: - user2374903
这看起来是一个不错的方法。 - Pointy
$.each(jArray, function(i, val) { setTimeout(function() { $('#reklame2').fadeOut("slow", function() { $(this).text(val).fadeIn("slow"); }); $('#reklame17').fadeOut("slow", function() { $(this).text(val).fadeIn("slow"); }); }, i * 6000); }); - user2374903
@user2374903:意图是什么?每个“partnr”是否应该更新一个单独的容器,还是它们都应该按顺序运行?或者应该是“partnr1 [0] -> partnr2 [0] -> partnr1 [1] -> partnr2 [1] ->等等”。 - user1106925
抱歉没有清楚地说明迭代的对象。每次 partnr 出现时,所有相应的模型都将作为一个列表在那一刻淡入。 - user2374903

0

这可以使用JavaScript的Map对象轻松实现。您只需迭代Map,利用正在迭代的map作为每次迭代调用中的参数即可。请注意在forEach函数中的map参数。这是您正在迭代的同一Map对象。


// Define the map
const myMap = new Map([
    ["key1", "value 1"],
    ["key2": "value 2"],
    ["key3": "value 3"]
])

// Iterate over the map, updating each value
myMap.forEach((value,key,map) => {
   map.set(key, value + "A")
})

/*
Result: myMap now looks like this:
[ 
    ["key1", "value 1A"],
    ["key2": "value 2A"],
    ["key3": "value 3A"]
]
/*

0
我们可以使用ES6版本中的forEach方法来操作地图。

var myMap =new Map([
    ["partnr1", ['modelA', 'modelB', 'modelC']],
    ["partnr2", ['modelA', 'modelB', 'modelC']]
]);

myMap.forEach(function(values,key){
  
  console.log(key);
  
  /*****Do something with the models***********/
  
  for(const [index,value] of values.entries()){
      console.log(`   ${key}[${index}] : ${value}`);
  }
  });


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