【问题标题】:Mapping array of objects to array of arrays with headers using ES6使用 ES6 将对象数组映射到带有标题的数组数组
【发布时间】:2017-11-04 04:23:29
【问题描述】:

我想将从后端的json 响应中获得的对象数组映射到数组数组,第一行是标题(标题)数组。我将使用此数组使其可在 csv 文件中下载。

另外,我想保留一些对于最终用户来说并不真正有趣的标题/列,以便在他们的 csv 文件中拥有。

我的代码运行良好,但我认为可以使用更简洁的代码来完成。我对使用 ES6 / ES2015 很好,但我自己并没有真正体验过扩展语法和其他 ES6 好东西,因此非常感谢任何关于更好、更现代(功能性/反应性?)方法的建议。

const originalData = [
  {name: 'Gizmo', species: 'cat', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping'},
  {name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail'},
  {name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing'}
]

let headers = []
const firstRow = originalData[0]
for (var key in firstRow) {
  if (firstRow.hasOwnProperty(key)) {
    if (!['raw','updated_at'].includes(key)) {
      headers.push(key)
    }
  }
}

const d = originalData.map(function(_, i) {
  return headers.map(function(header) {
    return originalData[i][header]
  }.bind(this))
}.bind(this))

const result = [headers].concat(d)

console.log(result)

【问题讨论】:

  • "我的代码运行良好" 那么您的问题可能会更好地放在Code Review 上,但在这样做之前请阅读他们的帮助中心。
  • @MikeMcCaughan,不知道代码审查,谢谢。

标签: javascript arrays functional-programming ecmascript-6 concat


【解决方案1】:

我想指出,对象的键顺序并非完全由规范“固定”。

如果您的originalData 中的第一个动物以species 属性开头,则您的整个表格将按该列顺序格式化...

因此,我建议您在数组中明确定义列,其中的顺序确实很重要。

请注意,在下面的示例中,我交换了 Gizmo 的属性声明顺序。将此数据放入您自己的代码中,第一列将是物种。 (至少在我的浏览器中是这样,我猜浏览器之间甚至会有所不同?)

const data = [
  {species: 'cat', name: 'Gizmo', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping'},
  {name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail'},
  {name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing'}
]


const getProps = props => obj => 
  props.map(k => obj[k]);
  
const columns = ["name", "species", "age", "skill"];

console.log(
  [columns, ...data.map(getProps(columns))]
);

【讨论】:

  • 是的,我知道这一点,但还是感谢您指出这一点。出于这个原因,我知道通常首选手动声明标头,但在我的情况下,实际的标头可能会发生很大变化(因为它是一个快速开发的 MVP)并且 csv 输出可以是“粗略的”,只要它可用为最终用户。
【解决方案2】:

使用过滤器和减少的完整链中的单行代码。

var unborken = chain => chain.filter((_, i, xx) => 
delete xx[i].updated_at && delete xx[i].raw).reduce((aac, _, i, aa) => 
(i === 0 ? aac.push(Object.keys(aa[i])) && aac.push(Object.values(aa[i])) :
aac.push(Object.values(aa[i])), aac), []);

const originalData = [{
    name: 'Gizmo',
    species: 'cat',
    age: '9',
    raw: 'G9e76rd',
    updated_at: '1318874398806',
    skill: 'sleeping'
  },
  {
    name: 'Benny',
    species: 'dog',
    age: '3',
    raw: '98HDo2h',
    updated_at: '1318874392417',
    skill: 'chasing tail'
  },
  {
    name: 'Oscar',
    species: 'cat',
    age: '2',
    raw: '9da8Ro1',
    updated_at: '1318874390283',
    skill: 'meowing'
  }
];


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

【讨论】:

    【解决方案3】:

    尝试使用 Array#map 使用 Object.keyvalue 重新创建数组。和new Set() 方法用户用于创建密钥集值。忽略重复的...spread syntax

    const originalData = [
      {name: 'Gizmo', species: 'cat', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping'},
      {name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail'},
      {name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing'}
    ]
    
    var result = [[...new Set(...originalData.map(a=> Object.keys(a)))]].concat(originalData.map(a=> Object.values(a)))
    
    console.log(result)
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    【讨论】:

      【解决方案4】:

      基本上,您可以对过滤后的键使用闭包并映射和连接数组。

      const fn = (array => (keys => [keys].concat(array.map(o => keys.map(k => o[k]))))
                 (Object.keys(array[0]).filter(k => !['raw','updated_at'].includes(k)))),
            data = [{ name: 'Gizmo', species: 'cat', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping' }, { name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail' }, { name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing' }],
            result = fn(data);
      
      
      console.log(result);
      .as-console-wrapper { max-height: 100% !important; top: 0; }

      【讨论】:

      • 既然你是柯里化数组,你需要一个闭包吗?
      • @Arrow,我需要关闭钥匙。
      • 你说得对,你确实需要那个闭包。我正在考虑这样做: const keys = data=> Object.keys(data[0]).filter(k => !['raw','updated_at'].includes(k)), table = data => (keys => [keys].concat(data.map(o => keys.map(k => o[k]))))(keys(data));console.log(table(data)) ;然而,我最终仍然需要那个闭包,我创建了一个额外的变量,我的错。
      【解决方案5】:

      你的很好。您可以使用Object.keys

      来简化标头的创建
      const originalData = [
        {name: 'Gizmo', species: 'cat', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping'},
        {name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail'},
        {name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing'}
      ]
      
      const headers = Object.keys(originalData[0])
          .filter(key => !['raw','updated_at'].includes(key)));  
      const data = originalData.map(row => headers.map(header => row[header]));
      
      console.log(headers, data);
      

      【讨论】:

        【解决方案6】:

        我就是这样做的。我想如果你知道你在使用哪些键,那么我们可以很好地利用它。

        const data = [
          {name: 'Gizmo', species: 'cat', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping'},
          {name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail'},
          {name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing'}
        ]
        
        const desiredKeys = ['name', 'species', 'age', 'skill']
        
        const result = [desiredKeys].concat(data.map(pet => desiredKeys.map(key => pet[key])))
        
        console.log(result)
        

        【讨论】:

          【解决方案7】:
          const originalData = [
            {name: 'Gizmo', species: 'cat', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping'},
            {name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail'},
            {name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing'}
          ];
          
          const propertiesNeeded = Object.keys(originalData[0]).filter(prop => !['raw', 'updated_at'].includes(prop));
          const dataMapped = originalData.map(obj => propertiesNeeded.map(prop => obj[prop]));
          const finalArr = [propertiesNeeded, ...dataMapped];
          

          【讨论】:

            【解决方案8】:

            这样的?

            const originalData = [
              { name: 'Gizmo', species: 'cat', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping' },
              { name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail' },
              { name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing' }
            ]
            
            const headers = Object.keys(originalData[0]).filter(key => !['raw', 'updated_at'].includes(key));
            const d = originalData.map(obj => headers.map(key => obj[key]))
            const result = [headers, ...d];
            
            console.log(result)
            

            【讨论】:

            • 太棒了。这个结合了更简单的标题创建和使用扩展运算符以正确的方式连接标题和内容。谢谢!
            猜你喜欢
            • 2020-03-19
            • 1970-01-01
            • 2021-04-18
            • 2020-07-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多