如何将带有键的对象数组发送到API?

5

我有几个标记:

<span class="first" data-id="1" />
<span class="second" data-id="4" />
<span class="second" data-id="2" />
<span class="third" data-id="5" />

对它们进行的AND操作:

const spans = document.querySelectorAll('span');

const list = [];

spans.forEach(function(span) {
    if (typeof list[span.getAttribute('class')] === 'undefined') {
    list[span.getAttribute('class')] = [];
  }

  list[span.getAttribute('class')].push(span.getAttribute('data-id'));
});

console.log(list);
console.log(JSON.stringify(list));

但是 JSON.stringify 返回空数组。

最简单的方式是如何统计给定 SPAN 中 data-id 的出现次数,然后将其转换为字符串?我想将这些数据发送到 API。

Fiddle: https://jsfiddle.net/x7thc59v/


尝试使用 const list = {},JSON.stringify 将返回空数组,对于任何非数字索引。而且你可以使用 const obj = {} 代替 list,因为你不想要列表,你想要对象。 - jcubic
4个回答

2

以下是可用的代码:

使用对象而不是数组来设置键。

使用var而不是const来修改变量。

最初的回答:

const spans = document.querySelectorAll('span');

var list = {};

spans.forEach(function(span) {
    if (typeof list[span.getAttribute('class')] === 'undefined') {
    list[span.getAttribute('class')] = [];
  }

  list[span.getAttribute('class')].push(span.getAttribute('data-id'));
});

console.log(list);
console.log(JSON.stringify(list));

1
不需要使用 var 而是使用 const - const 数组或对象的内容仍然可以被修改。 - Alnitak
是的,但这是我的观点,对于我的代码,如果我知道这个变量不需要更改,我会使用 const。如果变量将在任何时候更改,则使用 var,并在 for 循环中使用 let。(我重申这是我的做事方式,但你是正确的) - NoxFly
在你自己的代码中做你想做的事情,但请不要告诉别人不必要地这样做。 - Alnitak

2
如果你想要输出如下的结果 [{"first":["1"]},{"second":["4","2"]},{"third":["5"]}],那么可以按照以下步骤操作:
const spans = document.querySelectorAll('span');
const list = [];
spans.forEach(function(span) {
 const className = span.getAttribute('class');
 const valIndex = list.findIndex(val => val[className]);
 const hasVal = valIndex !== -1;
 if (className && hasVal) {
  const preVal = list[valIndex][className];
  list[valIndex][className] = preVal.concat(span.getAttribute('data-id'));
 } else if (className && !hasVal){
  list.push({[className]: [span.getAttribute('data-id')]});
 }
});
console.log(list);
console.log(JSON.stringify(list));

这里是一个可用的jsfiddle: 链接。最初的回答。

你的逻辑是不正确的 - OP 代码中的分支存在是为了确保在观察到特定类别时第一次存在一个空数组,以便他们可以将第一个元素推入与该类别关联的数组中。 - Alnitak
@Alnitak 更新了检查先前存在的键的逻辑,问题陈述不太清楚,但我认为逻辑只会考虑第一个键值,如果有多个。 - Mohit Yadav
仍然不正确 - OP 正试图创建一个结构,其中类名用作键,值是具有该类的所有元素的 data-id 字段的 _数组_。您的代码为每个 span 创建了单独的列表条目。 - Alnitak
@Alnitak 感谢您的建议,根据预期行为更新了解决方案,也许我在前两次尝试中误解了问题。 - Mohit Yadav
@Alnitak请在看清问题清晰度之前不要对答案进行负评,就我而言,我不知道期望的输出结果,问题中也没有提到,因此,请不要对答案进行负评,而是先对问题进行负评或在问题中添加期望的输出结果,这样人们才能提供适当的答案。 - Mohit Yadav

1
我认为你想要将list作为一个对象,因为你试图通过类名访问list的属性。

此外,与其使用forEach来改变外部对象,不如在document.querySelectorAll()调用返回的NodeList上使用Array.prototype.reduce

const spans = document.querySelectorAll('span');   

//With Array.prototype.reduce
const list = Array.prototype.reduce.call(spans, function(acc, span) {
    const attr = span.getAttribute('class');
    const dataId = span.getAttribute('data-id');
    acc[attr] ? acc[attr].push(dataId) : (acc[attr] = [dataId]);
    return acc;
}, {});

console.log(list);
console.log(JSON.stringify(list));
<span class="first" data-id="1" />
<span class="second" data-id="4" />
<span class="second" data-id="2" />
<span class="third" data-id="5" />


.concat 重新创建一个新的数组不是一个好的实践。 - Alnitak
@Alnitak 是的,你说得对,我只是尽可能地试图减少原始数组的变异。 - Fullstack Guy
将一个元素推入现有数组比让浏览器创建一个包含所有元素的全新数组并丢弃原始数组要便宜得多。 - Alnitak
@Alnitak 谢谢您的建议,我会记在心里。我已经更新了我的代码。 - Fullstack Guy

1
JSON.stringify函数不会序列化添加到数组中的字符串键,只会序列化数字键。因此,一个简单的解决方法是将const list = []替换为const list = {},然后更新使用结果的代码,以期望得到一个Object而不是一个Array
更一般地说,应该减少代码中的重复部分,特别是重复调用span.getAttribute('class')的部分。
const spans = document.querySelectorAll('span');
const list = {};

spans.forEach(function(span) {
    const cls = span.className;
    const id = span.getAttribute('data-id');

    list[cls] = list[cls] || [];   // ensure the array exists
    list[cls].push(id);
});

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