问题
I have a React component with a sorting headers table. This code works as planned:
//definitions and imports...
const [sort, setSort] = useState({column: 'rank', direction: 'ascending', isNumber: true});
const handleSort = (column, isNumber = false) => () => {
let direction = (sort.column !== column) ? 'ascending' : (sort.direction === 'ascending' ? 'descending' : 'ascending');
setSort({column, direction, isNumber});
};
const getSortedData = (data) => {
let sorted = R.sortBy(
R.compose(
sort.isNumber ? Number.parseFloat : R.toLower,
R.prop(sort.column)
),
data
);
return sort.direction === 'ascending' ? sorted : sorted.reverse();
}
//rest of the component...
However, I'm sure there's a way to use R.compose
to conditionally ascend
or descend
the sort (based on the sort.direction
property), and thus allow me to remove the last line of getSortedData
and the reverse
array op.
I tried several things (like composing ascend
with prop
, using the conditional in the compose etc.), but it all breaks down the code.
While it works well now, can anyone help me make it more Ramda
ish?
Update - adding the solution here for posterity's sake:
const getSortedData = (data) => {
return R.sort(
(sort.direction === 'ascending' ? R.ascend : R.descend)(
R.compose(
sort.isNumber ? Number.parseFloat : R.toLower,
R.prop(sort.column)
)
)
)(data);
}
Things I've learned:
R.sort
returns a function, and not an array, so you need to senddata
in as a parameter to the whole function.R.ascend
/R.descend
also expect the function as a paremeter, and therefore should not be a parameter ofR.compose
on their own.
回答1:
I think I would do something like this, wrapping ascend
or descend
around the composition:
const makeSorter = (sortConfig) => R.sort (
(sortConfig.direction === 'descending' ? R.descend : R.ascend) ( R.compose (
sortConfig.isNumber ? Number.parseFloat : R.toLower,
R.prop (sortConfig.column)
))
)
const people = [
{name: 'fred', age: 25},
{name: 'barney', age: 28},
{name: 'wilma', age: 29},
{name: 'betty', age: 22}
]
const byName = {direction: 'descending', column: 'name', isNumber: false}
const getSortedData = makeSorter(byName)
console .log (
getSortedData (people)
)
const byAge = {direction: 'ascending', column: 'age', isNumber: true}
console .log (
makeSorter (byAge) (people)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
I would personally prefer to write it with destructuring, as such:
const makeSorter = ({direction, isNumber, column}) => R.sort (
(direction === 'descending' ? R.descend : R.ascend) ( R.compose (
isNumber ? Number.parseFloat : R.toLower,
R.prop (column)
))
)
but that doesn't change anything fundamental.
回答2:
You can use ascend or descend with converge
:
- The first branch works out whether that's an ascending or descending sort
- The second branch works out which field (and which transformation) the sort should operate on
const arr = [{a: 10, b: 'ZZZ'}, {a: 5, b: 'ccc'}, {a: 11, b: 'aaa'}];
const makeSort = converge(call, [
({dir}) => dir === 'ascending' ? ascend : descend,
({isNumber, key}) => isNumber ? compose(parseFloat, prop(key)) : compose(toLower, prop(key))]);
const ascSortB = makeSort({dir: 'ascending', isNumber: false, key: 'b'});
const descSortA = makeSort({dir: 'descending', isNumber: true, key: 'a'});
console.log(sort(ascSortB, arr));
console.log(sort(descSortA, arr));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {sort, ascend, descend, prop, converge, call, compose, toLower} = R;</script>
You may also find this post useful
来源:https://stackoverflow.com/questions/56463740/how-to-compose-an-ascending-descending-sort-in-ramda