简体   繁体   English

递归 function 在对象数组中展平 object

[英]Recursive function that flattens an object within an array of objects

So I have an array of objects that I receive like this:所以我有一个像这样收到的对象数组:

[
  {
    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',
    },
  },
];

And I need an output like this:我需要一个像这样的 output :

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

How could I resolve it?我该如何解决? The array of objects doesn't always have the same shape so I need a recursive function that holds the first key from the original object and flatten the value until it is no more an object.对象数组并不总是具有相同的形状,所以我需要一个递归的 function 来保存原始 object 的第一个键并将值展平,直到它不再是 ZA8CFDE6331BD59EB2AC96F8911C4B66

There are a number of libraries that might help, but maybe something like this would work?有许多库可能会有所帮助,但也许这样的东西会起作用?

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

Except that you said "The array of objects doesn't always have the same shape", but I'm not sure how to address that.除了你说“对象数组并不总是具有相同的形状”,但我不知道如何解决这个问题。 You said something about searching recursively, does this mean looking for the deepest "value" in the object?你说过递归搜索,这是否意味着在 object 中寻找最深的“价值”?

This code should iteratively pull out the deepest value of each key in each object:此代码应该迭代地提取每个 object 中每个键的最深值:

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;
    }, {}));
  }, []);

You can do something like this, check if the value is an object or not, if it's object go to deeper level else use the value你可以这样做,检查value是否是 object,如果是 object go 到更深层次,否则使用该值

 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)

I would choose a version like this:我会选择这样的版本:

 // 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))

We can build this in a step-by-step manner:我们可以逐步构建它:

We start by writing a simple recursive deepestValue function like this:我们首先编写一个简单的递归deepestValue function,如下所示:

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

That could be used to write our transformation function this way:这可以用来编写我们的转换 function 这样的方式:

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

(If your environment does not support the relatively new Object.fromEntries , it's quite easy to shim.) (如果您的环境不支持相对较新的Object.fromEntries ,则很容易填充。)

We could stop there.我们可以停在那里。 But it's worth noting that this entries -> map -> fromEntries dance is a very reusable pattern.但值得注意的是,这个entries -> map -> fromEntries dance 是一个非常可重用的模式。 It's basically mapping a function over the properties of an object.它基本上是将 function 映射到 object 的属性上。 Perhaps a good name is mapObject .也许一个好名字是mapObject We can simply extract that and use it like this:我们可以简单地提取它并像这样使用它:

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

const transform = (xs) => xs .map (mapObject (deepestValue))

But there is another function we might want to abstract out of this, a map function that works like Array.prototype.map but which is a stand-alone function. But there is another function we might want to abstract out of this, a map function that works like Array.prototype.map but which is a stand-alone function. That is also very easy to write.这也很容易写。

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

(I don't write simply (fn) => (xs) => xs.map (fn) for reasons described many places, including in Why does parseInt yield NaN with Array#map? .) (我不简单地写(fn) => (xs) => xs.map (fn)原因描述了很多地方,包括在Why does parseInt yield NaN with Array#map?中。)

With this, we can now write the snippet above.有了这个,我们现在可以编写上面的代码片段了。

Functions such as map and mapObject can go in our personal utility libraries to be reused in various places in our application. mapmapObject等函数可以 go 在我们的个人实用程序库中,以便在我们应用程序的各个地方重用。 Libraries such as Underscore or Ramda (disclaimer: I'm a Ramda author) collect such things into useful collections. UnderscoreRamda等库(免责声明:我是 Ramda 作者)将这些内容收集到有用的 collections 中。 Ramda's map actually covers both these cases, so in Ramda we might write const transform = map (map (deepestValue)) . Ramda 的map实际上涵盖了这两种情况,所以在 Ramda 中我们可以写成const transform = map (map (deepestValue))

Note that I would not extract these helper functions on the basis of single cases.请注意,我不会基于单个案例提取这些辅助函数。 If I'd never seen the pattern before, I would be perfectly happy with the first version of transform .如果我以前从未见过这种模式,我会对第一个版本的transform感到非常满意。 But I've done similar things often enough that this abstraction makes sense to me.但是我经常做类似的事情,这种抽象对我来说是有意义的。 And I think it always helps to break things down into really simple pieces.而且我认为将事情分解成非常简单的部分总是有帮助的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM