如何在JavaScript中将键值对象数组转换为二维数组

3
let th = ['name', 'id', 'class']
    
let arr = [
    { 'name' : 'a', 'class':'7', 'id':'1' },
    { 'name' : 'b', 'class':'7', 'id':'2' },
    { 'name' : 'b', 'class':'7', 'id':'3' },
    { 'name' : 'd', 'class':'7', 'id':'4' }
]
    
/* final array what is look like */
    
let finalArr = [
    ['a', '1', '7'],
    ['b', '2', '7'],
    ['c', '3', '7'],
    ['d', '4', '7']
]

如何在不使用forEachmap的情况下创建这个数组?这只是一个示例数据,但实际上每次有超过10k个数据,所以forEach不是一个好选择。

2
如果你想要将数组作为最终输出,那么你需要遍历数据,而.map()是正确的工具(for循环可能更快,但差别不大)。如果你不想遍历整个数据集,那么你可以考虑只在访问/需要值时转换对象,否则,你可能需要考虑更改数据源或使用较小的数据块。 - Nick Parsons
我需要这些数据来打印PDF文件,如果我使用foreach循环,编译所有数据需要很长时间。我正在尝试创建一个动态数据循环,这样我只需要传递键,系统将自动从数组中选择它们各自的值。 - Suraj
1
我不明白为什么由于数据量大,for(each)循环不是一个好选择。如果你想转换数据结构,你必须以某种方式迭代所有数据。 - Ivar
5个回答

5

You have a typo in the third row, fixed here:

let th = ['name', 'id', 'class'];

let arr = [
    { 'name': 'a', 'class': '7', 'id': '1' },
    { 'name': 'b', 'class': '7', 'id': '2' },
    { 'name': 'c', 'class': '7', 'id': '3' },
    { 'name': 'd', 'class': '7', 'id': '4' }
];

const transformedArr = arr.map(row => th.map(name => row[name]));
console.log(transformedArr);

一个可能更快的解决方案,但实际上比上面的解决方案慢了2倍(在node.js / Chrome中测试了40000000个项目的arr,在Firefox中看起来相同,但在这些数据量下会挂起):

let th = ['name', 'id', 'class'];

let arr = [
    { 'name': 'a', 'class': '7', 'id': '1' },
    { 'name': 'b', 'class': '7', 'id': '2' },
    { 'name': 'c', 'class': '7', 'id': '3' },
    { 'name': 'd', 'class': '7', 'id': '4' }
];

const transformedArr = [];

for (let i = 0; i < arr.length; i++) {
    const item = [];
    for (let j = 0; j < th.length; j++) {
        item.push(arr[i][th[j]]);
    }
    transformedArr.push(item);
}

console.log(transformedArr);


1
你在内部循环中重载了外部循环的len变量。而且这不是常规的for循环语法。 - Drarig29
1
你应该使用通常的 let i = 0; i < arr.length; i++。内部循环的语法也相同。 - Drarig29
@Drarig29 len 是局部于每个循环的,所以这里没有问题,与外层循环中的 i 相反。当你把数组的长度属性缓存到一个变量中时,循环会稍微快一点(尤其是对于 Firefox 这样的优化),因为对象属性访问会稍微慢一些。 - Alexander Nenashev
1
首先,是的len是本地变量,所以没有冲突。但是变量重载意味着在作用域和其子作用域中使用相同的名称。它可以工作,但通常这是一种不好的做法。其次,你正在进行过早的优化,而且你不应该给初学者这个东西,除非有实际的基准测试和证明它更快。这里有一个人得出结论说它不会更快的例子:https://techformist.com/for-loop-array-length-cache-javascript/ - Drarig29
@Drarig29,冷静点,我已经接受了你的代码,虽然在那个特定情况下我不关心变量重载。谢谢你提供的链接,我猜我的基准测试至少在Chrome中已经过时了。实际上,我已经对两种解决方案进行了40000000个数组项的基准测试,并编辑了帖子-第二种解决方案速度较慢,尽管可能在3年前它会更快。基准测试总是随着浏览器版本的更改而变化。 - Alexander Nenashev
1
抱歉,我不想听起来刻薄。^^ - Drarig29

1
您可能会对Array.map/Array.forEach(特别是map)的性能感到惊讶。以下是不同方法的性能测试结果:

const arr4Test = [...Array(10000)].map((_, i) => ({name: `a`, class: 1, id: i}));
let finalArr = [];

// for ... of
let perf = performance.now();
for (let obj of arr4Test) {
  finalArr.push(Object.values(obj));
}
perf = (performance.now() - perf).toFixed(5);
console.log(`for ... of ${JSON.stringify(finalArr.slice(0, 3))} ... ${perf}ms`);

// classic loop
perf = performance.now();
finalArr.length = 0;
for (let i = 0; i < arr4Test.length; i += 1) {
  let tmpArr = [];
  
  for (let key in arr4Test[i]) {
    tmpArr.push(arr4Test[i][key])
  }
  
  finalArr.push(tmpArr);
}

perf = (performance.now() - perf).toFixed(5);
console.log(`classic loop ${JSON.stringify(finalArr.slice(0, 3))} ... ${perf}ms`);

// map
perf = performance.now();
finalArr = arr4Test.map(Object.values);
perf = (performance.now() - perf).toFixed(5);
console.log(`map => ${JSON.stringify(finalArr.slice(0, 3))} ... ${perf}ms`);

// forEach
finalArr.length = 0;
perf = performance.now();
arr4Test.forEach(v => finalArr.push(Object.values(v)));
perf = (performance.now() - perf).toFixed(5);
console.log(`forEach => ${JSON.stringify(finalArr.slice(0, 3))} ... ${perf}ms`);


经过对选项进行基准测试,我认为经典的循环测试不太好,应该使用Object.values以及其他测试中使用的方法,否则条件就不公平了。我添加了一个带有基准测试结果的答案。 - Alexander Nenashev

0
关于Kooilinc的回答。对选项进行了基准测试,我认为经典循环测试不好,应该像其他测试一样使用Object.values,否则条件不相等。

<script name="benchmark" data-count="1000">

const arr4Test = [...Array(10000)].map((_, i) => ({name: `a`, class: 1, id: i}));
let finalArr = [];

// @benchmark for...off
finalArr.length = 0;
for (let obj of arr4Test) {
  finalArr.push(Object.values(obj));
}
finalArr;

// @benchmark classic loop
finalArr.length = 0;
for (let i = 0; i < arr4Test.length; i += 1) {
  let tmpArr = [];
  
  for (let key in arr4Test[i]) {
    tmpArr.push(arr4Test[i][key])
  }
  
  finalArr.push(tmpArr);
}
finalArr;

// @benchmark map
finalArr = arr4Test.map(Object.values);
finalArr;

// @benchmark forEach
finalArr.length = 0;
arr4Test.forEach(v => finalArr.push(Object.values(v)));
finalArr;

</script>
<script src="https://raw.githack.com/silentmantra/benchmark/master/benchmark.min.js"></script>


0

let th = ['name', 'id', 'class']

let arr = [{
    'name': 'a',
    'class': '7',
    'id': '1'
  },
  {
    'name': 'b',
    'class': '7',
    'id': '2'
  },
  {
    'name': 'b',
    'class': '7',
    'id': '3'
  },
  {
    'name': 'd',
    'class': '7',
    'id': '4'
  }
];

let data = arr.map((e) => ([e.name, e.id, e.class]));
console.log(data);

let data1 = arr.map((e) => th.map((j) => e[j]))
console.log(data1);

console.log(arr.map((e) => (Object.values(e))));


0
你只需要使用.map将一个新的数组推送到finalArr中,并按照你想要的列的顺序。
let arr = [
    { 'name' : 'a', 'class':'7', 'id':'1' },
    { 'name' : 'b', 'class':'7', 'id':'2' },
    { 'name' : 'b', 'class':'7', 'id':'3' },
    { 'name' : 'd', 'class':'7', 'id':'4' }
    ]
    
let finalArr = [];

arr.map(row => {
  finalArr.push([row.name, row.id, row.class]);
})

console.log(finalArr);

1
如果您不使用map()的返回值,应该使用forEach()代替。所以要么使用arr.forEach(row => finalArr.push([row.name, row.id, row.class])),要么使用finalArr = arr.map(row => [row.name, row.id, row.class])。不要仅仅为了迭代而使用map(),这不是它的设计目的。 - 3limin4t0r

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