问题
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 Ramdaish?
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.sortreturns a function, and not an array, so you need to senddatain as a parameter to the whole function.R.ascend/R.descendalso expect the function as a paremeter, and therefore should not be a parameter ofR.composeon 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