This is a follow-up question to: How to build/transform an object from a previous object
Given data in a key-value format, I want to transform it into a new structure.
// given data example #1
const dataAnimals = {"monkeys": 10, "alligators": 20}
// given data example #2
const dataColors = {"red": 100, "green": 4, "yellow": 17}
And the new structure of the output can be chosen from any prespecified format.
If we stick with dataAnimals
as the input, there are three possible output formats:
structure type 1
[
{
label: {
en: "monkeys"
},
value: 10
},
{
label: {
en: "alligators"
},
value: 20
}
]
structure type 2
[
{
count: 10,
label: {
en: "monkeys"
}
},
{
count: 20,
label: {
en: "alligators"
}
}
]
structure type 3
[
{
count: 10,
value: "monkeys"
},
{
count: 20,
value: "alligators"
}
]
I'm likely to have several more types of structures.
I wonder if there's a way to supply the chosen/desired structure (i.e., pass the structure to a builder function alongside the input data itself).
So far, using ramda
I could only write 3 separate builder functions with the structure being hard-coded inside each:
const buildArr1 = R.pipe(R.toPairs, R.map(R.applySpec({value: R.nth(1), label: {en: R.nth(0)}})))
const buildArr2 = R.pipe(R.toPairs, R.map(R.applySpec({count: R.nth(1), label: {en: R.nth(0)}})))
const buildArr3 = R.pipe(R.toPairs, R.map(R.applySpec({value: R.nth(0), count: R.nth(1)})))
buildArr1(dataAnimals)
buildArr2(dataAnimals)
buildArr3(dataAnimals)
The differences between buildArr1()
, buildArr2()
, and buildArr3()
are subtle and only in terms of the structure inside applySpec()
. So it got me thinking, whether it's possible to have just one function buildArr()
, that takes as input both the data to transform and the format to be returned.
// pseudo-code for desired code
buildArr(dataAnimals, structureType1)
// which will return
// [
// {
// label: {
// en: "monkeys"
// },
// value: 10
// },
// {
// label: {
// en: "alligators"
// },
// value: 20
// }
// ]
buildArr(dataAnimals, structureType2)
// [
// {
// count: 10,
// label: {
// en: "monkeys"
// }
// },
// {
// count: 20,
// label: {
// en: "alligators"
// }
// }
// ]
Is there any proper way to do something like that? I'm also not sure what should be the nature of structureType1
/structureType2
being passed. An object? Or rather a function that "knows to interact" with the input data?
Please note that while this question revolves around ramda
, it seems (to me) to be broader than a specific library.
EDIT
User @Scott Sauyet has nicely pointed out that writing the individual builders (i.e., buildArr1()
, buildArr2
, etc.) could be achieved with plain JS. This speaks to my point above, that this isn't necessarily a ramda
question.
const buildArr1PlainJS = (o) => ({data: Object .entries (o) .map (([k, v]) => ({value: v, label: {en: k}}))})
const buildArr2PlainJS = (o) => ({data: Object .entries (o) .map (([k, v]) => ({count: v, label: {en: k}}))})
const buildArr3PlainJS = (o) => ({data: Object .entries (o) .map (([k, v]) => ({value: k, count: v}))})
CodePudding user response:
I think you can do this fairly easily by passing in a formatting function to a generic processor. It might look something like this:
// main function
const transform = (fn) => (o) => Object .entries (o) .map (([k, v]) => fn (k, v))
// formatting functions
const format1 = transform ((k, v) => ({label: {en: k}, value: v}))
const format2 = transform ((k, v) => ({count: v, label: {en: k}}))
const format3 = transform ((k, v) => ({count: v, value: k}))
// input data
const inputs = {
dataAnimals: {"monkeys": 10, "alligators": 20},
dataColors: {"red": 100, "green": 4, "yellow": 17},
};
// demo
Object .entries (inputs) .forEach (([name, input]) => [format1, format2, format3] .forEach ((format, i) =>
console .log (`${name}, format ${i 1}:`, format (input))
))
.as-console-wrapper {max-height: 100% !important; top: 0}
Again, I don't see Ramda adding much value here, although we could of course do this with Ramda's tools. We might write:
const transform = (fn) => (o) => map (unapply (fn)) (toPairs (o))
Or if you had a point-free fetish, even this:
const transform = compose (flip (o) (toPairs), map, unapply)
But I don't think these -- especially the latter -- add anything useful.