Reputation: 4254
cons columnDefs = [
{
label: 'The_First_Name',
value: getProp,
args: ['firstName'] // different number of arguments depending on function
},
{
label: 'City',
value: getNestedProperty,
args: ['location', 'city']
}
]
const data = [
{
firstName: 'Joe',
lastName: 'Smith',
location: {
city: 'London'
}
},
{
firstName: 'Anna',
lastName: 'Andersson',
location: {
city: 'Stockholm'
}
}
]
const getProp = (object, key) => R.prop(key, object);
const getNestedProperty = (obj, args) => R.path(..args, obj);
Ramda pipe to map the data:
const tableBuilder = R.pipe(R.map); // some ramda functions in here
const rows = tableBuilder(data, columnDefs);
The wanted output:
rows output:
[
{
The_First_Name: 'Joe',
city: 'London'
},
{
The_First_Name: 'Anna',
city: 'Stockholm'
}
]
The key of each row is the label
property in the columnDefs
. The value is fetched from the Ramda
function in the value
prop together with the arguments defined in the args
prop.
https://plnkr.co/edit/rOGh4zkyOEF24TLaCZ4e?p=preview
Totally stuck. Is this even possible to do with Ramda? Or is better to do it in plain javascript?
Upvotes: 0
Views: 1181
Reputation: 50807
The following should work:
const tableBuilder = (objs, spec) => objs .map (
obj => Object .assign ( ...spec.map (
( {label, value, args} ) => ( { [label]: value (obj, args) } )
))
)
const getProp = (object, key) => R.prop (key, object);
const getNestedProperty = (obj, args) => R.path (args, obj);
const columnDefs = [
{label: 'The_First_Name', value: getProp, args: ['firstName']},
{label: 'City', value: getNestedProperty, args: ['location', 'city']}
]
const data = [
{firstName: 'Joe', lastName: 'Smith', location: {city: 'London'}},
{firstName: 'Anna', lastName: 'Andersson', location: {city: 'Stockholm'}}
]
console .log (
tableBuilder (data, columnDefs)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script> <script>
const {prop, path} = R </script>
But it's working mostly by happenstance. You might need to rethink a bit how your functions are defined.
This is calling the equivalent of prop (['firstName'], obj)
, which happens to work like prop ('firstName', obj)
, but only for the same reason that 'foo' + ['bar']
yields 'foobar'
. It's a coincidence you probably should not depend upon.
The trouble is that you want to treat uniformly functions that take a single argument and ones that take an array of arguments. This is a problem. You probably need to make this consistent.
While you can write a Ramda gloss on this, I'm not sure it will be any more readable. Perhaps replacing Object .assign (...spec.map (
with mergeAll (spec.map (
would be cleaner. And if you don't mind changing parameter orders, there is probably a bit more clean-up. But this is already fairly readable.
The answer from @customcommander convinced me that Ramda really could add some value here. This requires that you be willing to swap the parameter order for your value
functions, and to be willing to call it as a fully curried function (tableBuilder (columnDefs) (data)
), but it does lead to some pretty nice code.
This is mostly the work of customcommander, but I adjusted it a bit to make a more readable function:
const spec = ({label, value, args}) => ({[label]: value(args)})
const tableBuilder = pipe(
map(spec),
mergeAll,
applySpec,
map
)
const columnDefs = [
{label: 'The_First_Name', value: prop, args: ['firstName']},
{label: 'City', value: path, args: ['location', 'city']}
]
const data = [
{firstName: 'Joe', lastName: 'Smith', location: {city: 'London'}},
{firstName: 'Anna', lastName: 'Andersson', location: {city: 'Stockholm'}}
]
console .log (
tableBuilder (columnDefs) (data)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script><script>
const {prop, path, pipe, map, mergeAll, applySpec} = R </script>
This has the same issue as before with prop
, but do note that you can replace it here with path
without harm. The main point is that all your value
functions should have the same inputs (an array of values and the object to work on.) If they do, then this should work for any of them.
Upvotes: 0
Reputation: 18941
You can use applySpec
to create an object from another one:
const obj = applySpec({
The_First_Name: prop('firstName'),
city: path(['location', 'city'])
})
obj({
firstName: 'Joe',
lastName: 'Smith',
location: {
city: 'London'
}
});
//=> {"The_First_Name": "Joe", "city": "London"}
Then you can use that function to map over you array:
const data = [
{
firstName: 'Joe',
lastName: 'Smith',
location: {
city: 'London'
}
},
{
firstName: 'Anna',
lastName: 'Andersson',
location: {
city: 'Stockholm'
}
}
];
const obj = applySpec({
The_First_Name: prop('firstName'),
city: path(['location', 'city'])
})
console.log(
map(obj, data)
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {applySpec, prop, path, map} = R;</script>
This is how you transform columnDefs
into an object that you can use with applySpec
:
const spec = def => ({[def.label]: apply(def.value, def.args)});
const specs = compose(mergeAll, map(spec));
const columnDefs = [
{
label: 'The_First_Name',
value: prop,
args: ['firstName'] // different number of arguments depending on function
},
{
label: 'City',
value: path,
args: [['location', 'city']]
}
]
const data = [
{
firstName: 'Joe',
lastName: 'Smith',
location: {
city: 'London'
}
},
{
firstName: 'Anna',
lastName: 'Andersson',
location: {
city: 'Stockholm'
}
}
]
const spec = def => ({[def.label]: apply(def.value, def.args)});
const specs = compose(mergeAll, map(spec));
console.log(
map(applySpec(specs(columnDefs)), data)
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {apply, compose, mergeAll, map, prop, path, applySpec} = R;</script>
Upvotes: 1