【问题标题】:Compact Array of objects by value (Jquery or Underscore)按值排列的紧凑对象数组(Jquery 或下划线)
【发布时间】:2018-01-10 14:50:46
【问题描述】:

我有一个对象数组:

"Entries" : [ {
        "FieldName" : "Name1",
        "FieldValue" : "N1Value1"
    }, {
        "FieldName" : "Name1",
        "FieldValue" : "N1Value2"
    }, {
        "FieldName" : "Name1",
        "FieldValue" : "N1Value3"
    }, {
        "FieldName" : "Name2",
        "FieldValue" : "N2Value1"
    }, {
        "FieldName" : "Name2",
        "FieldValue" : "N2Value2"
    }, {
        "FieldName" : "Name2",
        "FieldValue" : "N2Value3"
    }, {
        "FieldName" : "Name3",
        "FieldValue" : "N3Value1"
    }, {
        "FieldName" : "Name4",
        "FieldValue" : "N4Value1"
    }, {
        "FieldName" : "Name5",
        "FieldValue" : "N5Value1"
    } ]

我想把它改成:

"Entries" : [ {
          "FieldName" : "Name1",
          "FieldValue" : ["N1Value1","N1Value2","N1Value3"] 
      }, {
          "FieldName" : "Name2",
          "FieldValue" : ["N2Value1", "N2Value2", "N2Value3"]
      }, {
          "FieldName" : "Name3",
          "FieldValue" : "N3Value1"
      }, {
          "FieldName" : "Name4",
          "FieldValue" : "N4Value1"
      }, {
          "FieldName" : "Name5",
          "FieldValue" : "N5Value1"
      } ]

我尝试首先在一个数组中拥有唯一的 FieldName 值,然后在 JSON 数组中使用 _.filter 以尝试接收一个数组作为回报,但我得到的只是整个对象。

    $.each(arrUniqueFieldNames, function() {
        var strFName = String(this);
        oEntries[this] = _.filter(jsonEntries, function (item) { 
            if (item.FieldName === strFName) {
                return String(item.FieldValue) 
            } 
        });
    });

这段代码可能有一些不完美之处,但它并没有像我预期的那样工作,因为它返回整个对象而不仅仅是值。

我还尝试将 _.filter 替换为 _.map,但结果是我为每个数组都得到了一个包含大量“未定义”的数组。

【问题讨论】:

  • 如果每个数组成员都有一个唯一的FieldName 属性,是否有理由需要数组而不是键/值存储,例如MapObject?你的结构可以是Entries: {Name1: [...values...], Name2: [...values...]} 等等。这使您可以更快地按名称查找。
  • 很遗憾我无法修改原始 JSON 的结构;否则,我已经得到了我想要的 :( FieldName 应该是唯一的,但它不是。
  • 我不是在谈论原始数据。我的意思是你想做的转变。您可以将其转换为我上面描述的结构而不是数组。如果您实际上不需要数组,那么这将提供非常快速的名称查找。
  • ...FWIW,我会让FieldValue 保持一致,这样它就始终是一个数组,即使其中只有一个值。
  • 不幸的是,我在其中的角色只是将 JSON 修复为预期接收的格式,我也无权知道它是如何处理的!

标签: javascript arrays data-structures mapreduce underscore.js


【解决方案1】:

我知道你想要 jquery,但我对它不是很坚定,所以我给你一个纯 JavaScript 的解决方案:

test =  [ {
    "FieldName" : "Name1",
    "FieldValue" : "N1Value1"
    }, {
    "FieldName" : "Name1",
    "FieldValue" : "N1Value2"
    }, {
    "FieldName" : "Name1",
    "FieldValue" : "N1Value3"
    }, {
    "FieldName" : "Name2",
    "FieldValue" : "N2Value1"
    }, {
    "FieldName" : "Name2",
    "FieldValue" : "N2Value2"
    }, {
    "FieldName" : "Name2",
    "FieldValue" : "N2Value3"
    }, {
    "FieldName" : "Name3",
    "FieldValue" : "N3Value1"
    }, {
    "FieldName" : "Name4",
    "FieldValue" : "N4Value1"
    }, {
    "FieldName" : "Name5",
    "FieldValue" : "N5Value1"
    } ];

result =[];

for (i=0; i<test.length; i++)
{
    element=test[i];
    key=element['FieldName'];
    value=element['FieldValue'];
    if (!(key in result))
        result[key] = [value];
    else
        result[key].push(value);
}
console.log(result);

【讨论】:

  • 你不知道你的回答给了我多少安慰!想想我认为使用 jquery 或下划线会更简单!我完全可以使用纯 JavaScript 解决方案!如果有的话,这更具可读性和直截了当。真的很感谢!
  • @Paprika ...虽然这种方法乍一看给人留下了很好的印象,但我不认为它是精心设计的。它不会像 OP 所要求的那样返回按相同名称分组的新字段项数组。但它滥用result 数组作为非数字键值对的(普通)对象。因此这个数组的长度总是为零,因此不能被任何数组迭代器方法迭代。
  • @Peter Seliger 你的反对是对的,但是当你使用关联数组时这很正常:它们的长度总是0。如果你想遍历它,你可以这样做: for (键入结果)。此方法从 IE 9 开始可用。
  • @Sascha ... 首先,OP 希望将最终结果映射到数组中。这就是所要求的。在第 2 点,如果提供了类似 map/hash 的结构,那么出于可读性的原因已经应该使用普通对象而不是像数组这样的类似列表的结构。在第 3 点,即使使用对象可能更好,通过 Object.keys 迭代它们的键,然后使用一些额外的数组方法,而不是依赖笨拙的 for (key in result)。最后,对您的批评者完全符合 OP 规范的干净且有效的解决方案投反对票是缺乏体育精神。
  • @Peter Seliger ...我没有对你投反对票。我想您首先对我投了反对票,但几分钟后,其他人同时对我们俩投了反对票。如果没有任何理由意味着其中存在错误,我永远不会对某人投反对票。
【解决方案2】:

另一种可能更好的可重用/可维护解决方案,也是纯粹基于语言核心的,可以使用Array.prototype.reduce,然后可能看起来像这样......

var entries = [{
  "FieldName" : "Name1",
  "FieldValue": "N1Value1"
}, {
  "FieldName" : "Name1",
  "FieldValue": "N1Value2"
}, {
  "FieldName" : "Name1",
  "FieldValue": "N1Value3"
}, {
  "FieldName" : "Name2",
  "FieldValue": "N2Value1"
}, {
  "FieldName" : "Name2",
  "FieldValue": "N2Value2"
}, {
  "FieldName" : "Name2",
  "FieldValue": "N2Value3"
}, {
  "FieldName" : "Name3",
  "FieldValue": "N3Value1"
}, {
  "FieldName" : "Name4",
  "FieldValue": "N4Value1"
}, {
  "FieldName" : "Name5",
  "FieldValue": "N5Value1"
}];


function collectAndGroupValuesOfEquallyNamedFieldItems (collector, fieldItem) {
  var
    fieldName = fieldItem.FieldName,
    itemGroup = collector.map[fieldName];

  if (!itemGroup) {
    itemGroup  = collector.map[fieldName] = {

      "FieldName" : fieldName,
      "FieldValue": []
    };
    collector.list.push(itemGroup);
  }
  itemGroup.FieldValue.push(fieldItem.FieldValue);

  return collector;
}


entries = entries.reduce(collectAndGroupValuesOfEquallyNamedFieldItems, {

  map:  {},
  list: []

}).list;


console.log('entries : ', entries);
.as-console-wrapper { max-height: 100%!important; top: 0; }

附录/编辑

@Paprika ... 考虑到@rockstar 的最后评论,上述解决方案也可以识别FieldValue 的双重数据结构 - {String} 值用于单个 FieldValue 值和 {Array&lt;String&gt;} 用于多个FieldValue 值 - 确实更改为 ...

var entries = [{
  "FieldName" : "Name1",
  "FieldValue": "N1Value1"
}, {
  "FieldName" : "Name1",
  "FieldValue": "N1Value2"
}, {
  "FieldName" : "Name1",
  "FieldValue": "N1Value3"
}, {
  "FieldName" : "Name2",
  "FieldValue": "N2Value1"
}, {
  "FieldName" : "Name2",
  "FieldValue": "N2Value2"
}, {
  "FieldName" : "Name2",
  "FieldValue": "N2Value3"
}, {
  "FieldName" : "Name3",
  "FieldValue": "N3Value1"
}, {
  "FieldName" : "Name4",
  "FieldValue": "N4Value1"
}, {
  "FieldName" : "Name5",
  "FieldValue": "N5Value1"
}];


function collectAndGroupValuesOfEquallyNamedFieldItems (collector, fieldItem) {
  var
    fieldName = fieldItem.FieldName,
    itemGroup = collector.map[fieldName];

  if (!itemGroup) {
    itemGroup  = collector.map[fieldName] = {

      "FieldName" : fieldName,
      "FieldValue": null
    };
    collector.list.push(itemGroup);
  }
  if (Array.isArray(itemGroup.FieldValue)) { // list/array of multiple string values.

    itemGroup.FieldValue.push(fieldItem.FieldValue);

  } else if (typeof itemGroup.FieldValue === 'string') { // data structure switch.

    itemGroup.FieldValue = [itemGroup.FieldValue, fieldItem.FieldValue]

  } else if (itemGroup.FieldValue === null) { // initial string value.

    itemGroup.FieldValue = fieldItem.FieldValue;
  }
  return collector;
}


entries = entries.reduce(collectAndGroupValuesOfEquallyNamedFieldItems, {

  map:  {},
  list: []

}).list;


console.log('entries : ', entries);
.as-console-wrapper { max-height: 100%!important; top: 0; }

【讨论】:

  • 我只想指出,当FieldValue 只有一个项目而不是具有单个成员的数组时,OP 提供的结果有一个字符串。但后来我感觉 OP 不太清楚他想要什么。
  • 有一个附录考虑了@rockstar 的最后评论,并略微改变了第一个给定解决方案的实现,以识别"FieldValue"s 数据结构的双重性质。
猜你喜欢
  • 2014-01-17
  • 1970-01-01
  • 2016-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-09
  • 1970-01-01
相关资源
最近更新 更多