【问题标题】:Recursive function that flattens an object within an array of objects将对象数组中的对象展平的递归函数
【发布时间】:2023-11-02 01:32:02
【问题描述】:

所以我有一个像这样收到的对象数组:

[
  {
    accountName: {
      type: 'link',
      value: {
        value: '1234567890123456789',
        to: '/q?Id=1237896540789654',
      },
    },
    bank: {
      type: 'text',
      value: 'Foo Bank',
    },
  },
  {
    accountName: {
      type: 'link',
      value: {
        value: '9234567890123456789',
        to: '/q?Id=9234567890123456789',
      },
    },
    bank: {
      type: 'text',
      value: 'Foo Bank',
    },
  },
];

我需要这样的输出:

[
  {
    accountName: '1234567890123456789', 
    bank: 'Foo Bank'
  },
  {
    accountName: '9234567890123456789', 
    bank: 'Foo Bank'
  }
]

我该如何解决? 对象数组并不总是具有相同的形状,所以我需要一个递归函数来保存原始对象的第一个键并将值展平,直到它不再是一个对象。

【问题讨论】:

  • 认为这是一个 python 问题并在这里做了。 trinket.io/python/609679e8d9 哎呀。但也许它对某人有用
  • 你能给我们一个嵌套的例子,这样我们就可以明白你所说的不同形状是什么意思?

标签: javascript reactjs recursion flatten


【解决方案1】:

有许多库可能会有所帮助,但也许这样的东西会起作用?

results.map(result => ({
    accountName: result.accountName.value.value,
    bank: result.bank.value
}));

除了你说“对象数组并不总是具有相同的形状”,但我不知道如何解决这个问题。你说过递归搜索,这是否意味着在对象中寻找最深的“价值”?

【讨论】:

  • 是的,我正在寻找“最深”的值 此外,输出中的属性必须相应地命名为原始属性。它并不总是具有相同的属性。
  • @Kauyumari Code Maniac's answer 对我来说看起来不错。 IceMetalPunk's 也不错,但复杂度更高。如果其中一位回答了您的问题,请不要忘记接受。
【解决方案2】:

这段代码应该迭代地拉出每个对象中每个键的最深值:

const processed = data.reduce((result, obj) => {
  return result.concat(Object.keys(obj).reduce((entry, key) => {
      entry[key] = obj[key];
      while (entry[key].value) { entry[key] = entry[key].value; }
      return entry;
    }, {}));
  }, []);

【讨论】:

    【解决方案3】:

    你可以做这样的事情,检查value是否是一个对象,如果它是对象进入更深层次,否则使用该值

    let data = [{accountName: {type: 'link',value: {value: '1234567890123456789',to: '/q?Id=1237896540789654',},},bank: {type: 'text',value: 'Foo Bank',},},{accountName: {type: 'link',value: {value: '9234567890123456789',to: '/q?Id=9234567890123456789',},},bank: {type: 'text',value: 'Foo Bank',},},];
    
    let getDeepestValue = obj => {
      let value = obj.value
      while(typeof value == 'object'){
        value = value.value 
      }
      return value
    }
    
    let final = data.map(({accountName,bank})=>{
      return{
        accountName: getDeepestValue(accountName),
        bank: getDeepestValue(bank)
      }
    })
    
    console.log(final)

    【讨论】:

    • 我比 IceMetalPunk 的答案更喜欢这个,我觉得它更具可读性。
    • 很好,但请参阅我的答案以获取getDeepestValue 的递归替代方案。
    【解决方案4】:

    我会选择这样的版本:

    // helper functions
    const mapObject = (fn) => (o) => Object .fromEntries (
      Object .entries (o) .map (([k, v]) => [k, fn (v)])
    )
    const map = (fn) => (xs) => xs .map(x => fn (x))
    
    // main functions
    const deepestValue = (o) => 
      typeof o .value== "object" ? deepestValue (o .value) : o .value
    
    const transform = map (mapObject (deepestValue))
    
    // demo
    const input=[{accountName: {type: "link", value: {value: "1234567890123456789", to: "/q?Id=1237896540789654"}}, bank: {type: "text", value: "Foo Bank"}}, {accountName: {type: "link", value: {value: "9234567890123456789", to: "/q?Id=9234567890123456789"}}, bank: {type: "text", value: "Foo Bank"}}]
    
    console.log (transform (input))

    我们可以逐步构建它:

    我们首先编写一个简单的递归deepestValue 函数,如下所示:

    const deepestValue = (o) => 
      typeof o .value== "object" ? deepestValue (o .value) : o .value
    

    这可以用来编写我们的转换函数:

    const transform = (xs) => xs .map (x => Object.fromEntries (
      Object .entries (x) .map (([k, v]) => [k, deepestValue (v)])
    ))
    

    (如果您的环境does not support 是相对较新的Object.fromEntries,则很容易填充。)

    我们可以停在那里。但值得注意的是,这个entries->map->fromEntries dance 是一个非常可重用的模式。它基本上是将函数映射到对象的属性上。也许一个好名字是mapObject。我们可以简单地提取它并像这样使用它:

    const mapObject = (fn) => (o) => Object .fromEntries (
      Object .entries (o) .map (([k, v]) => [k, fn (v)])
    )
    
    const transform = (xs) => xs .map (mapObject (deepestValue))
    

    但我们可能还想从中抽象出另一个函数,一个 map 函数,它的工作原理与 Array.prototype.map 类似,但它是一个独立的函数。这也很容易写。

    const map = (fn) => (xs) => xs .map (x => fn (x))
    

    (我不会简单地写(fn) => (xs) => xs .map (fn),原因在很多地方都有描述,包括Why does parseInt yield NaN with Array#map?。)

    有了这个,我们现在可以写上面的sn-p了。

    mapmapObject 等函数可以放入我们的个人实用程序库中,以便在我们应用程序的各个地方重用。 UnderscoreRamda 之类的库(免责声明:我是 Ramda 的作者)将这些东西收集到有用的集合中。 Ramda 的map 实际上涵盖了这两种情况,所以在 Ramda 中我们可以写成const transform = map (map (deepestValue))

    请注意,我不会基于单个案例提取这些辅助函数。如果我以前从未见过这种模式,我会对transform 的第一个版本非常满意。但是我经常做类似的事情,这种抽象对我来说是有意义的。而且我认为将事情分解成非常简单的部分总是有帮助的。

    【讨论】:

      最近更新 更多