Reputation: 103
I am combining object values based on the names of the prefixes. All works great except there is a need for separator symbol with array output
const lookup = [
{prefix: "fruit", key: "sweet", "separator": "###" },
{prefix: "rate" }, //noseparator and no key should retain default prefix as rate
];
The above lookup defines which keys must be combined. If the keys are not specified then it means not to combine.
const input = [{
fruit_a: "red apple",
fruit_b: "green apple",
rate_a: "10$",
rate_b: "20$",
vegetable_a: "spinach",
vegetable_b: "carrot",
},
{
fruit_d: "white apple",
fruit_e: "yellow apple",
rate_d: "10$",
rate_e: "20$",
vegetable_d: "radish",
vegetable_e: "cabbage",
}
];
const reduced = Object.entries(input)
.reduce( (acc, [key, value]) => {
const nwKey = lookup.find(v => key.startsWith(v.prefix));
return nwKey
? { ...acc, [nwKey.key]: (acc[nwKey.key] || []).concat(value) }
: { ...acc, [key]: value };
}, {}
);
My incorrect code output is
{
"sweet": [
"red apple",
"green apple"
],
"ratex": [
"10$",
"20$"
],
"vegetable_a": "spinach",
"vegetable_b": "carrot"
}
expected output is
[{
"sweet": "red apple###green apple",
"rate": "10$,20$", //retain default prefix if not mentioned in lookup
"vegetable_a": "spinach",
"vegetable_b": "carrot"
}
{
"sweet": "white apple###yellow apple",
"rate": "10$,20$",//retain default prefix if not mentioned in lookup
"vegetable_a": "radish",
"vegetable_b": "cabbage"
}]
Upvotes: 0
Views: 449
Reputation: 1652
So the first issue from the code you provided is that you use Object.entries(input)
, but input
isn't an object but rather an array. So if you want to iterate and reduce each entry within input
, you should use input.map
first to alter each entry in input
, and then use Object.entries
on the entry to iterate each key and reduce it, like so:
const reduced = input.map(entry => Object.entries(entry).reduce(...))
This will allow you to reduce each entry in the input
array as expected.
I'd suggest reducing the amount of code at the ternary operator too. Ternary operators are great, but they can also quickly become complicated and unreadable in my opinion. And if you're also using them for return statements, they should be quite simple. Otherwise, it might be hard to understand what is being returned at each condition.
Your code for finding an entry in the lookup based on the key and prefix works just fine, but if it isn't able to find the key in the lookup table, it should just return immediately, like so:
const nwKey = lookup.find(v => key.startsWith(v.prefix));
if (!nwKey) return { ...acc, [key]: value }
Your example doesn't include the separator from the lookup, so I'm guessing this is also where you're having issues. Finding the new value and deciding if it needs a separator or not should probably be done as a separate variable instead of adding it to the final return statement. It will make it a lot more readable. We can use ternary operators again here, without it being too complicated this time.
const nwValue = acc[nwKey.key]
? acc[nwKey.key] + (nwKey.separator || ',') + value
: value
And finally, we need of course need to return the accumulated object with the key and the new value:
return {
...acc,
[nwKey.key]: nwValue
}
So with everything together, the code looks like this:
const reduced = input
.map(entry => Object.entries(entry).reduce((acc, [key, value]) => {
const nwKey = lookup.find(v => key.startsWith(v.prefix));
if (!nwKey) return { ...acc, [key]: value }
const nwValue = acc[nwKey.key]
? acc[nwKey.key] + (nwKey.separator || ',') + value
: value
return {
...acc,
[nwKey.key]: nwValue
}
}, {})
);
Which produces:
[
{
sweet: 'red apple###green apple',
ratex: '10$,20$',
vegetable_a: 'spinach',
vegetable_b: 'carrot'
},
{
sweet: 'white apple###yellow apple',
ratex: '10$,20$',
vegetable_d: 'radish',
vegetable_e: 'cabbage'
}
]
Upvotes: 1