Reputation: 31
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:
[
{
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.
Upvotes: 2
Views: 92
Reputation: 50807
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:
const deepestValue = (o) =>
typeof o .value== "object" ? deepestValue (o .value) : o .value
That could be used to write our transformation function this way:
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.)
We could stop there. But it's worth noting that this entries
->map
->fromEntries
dance is a very reusable pattern. It's basically mapping a function over the properties of an object. Perhaps a good name is 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. 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?.)
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. Libraries such as Underscore or Ramda (disclaimer: I'm a Ramda author) collect such things into useful collections. Ramda's map
actually covers both these cases, so in Ramda we might write 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
. 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.
Upvotes: 0
Reputation: 37755
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
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)
Upvotes: 2
Reputation: 5556
This code should iteratively pull out the deepest value of each key in each 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;
}, {}));
}, []);
Upvotes: 2
Reputation: 11382
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?
Upvotes: 2