How to compose an ascending/descending sort in Ramda

本秂侑毒 提交于 2021-01-28 08:32:57

问题


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:

  1. R.sort returns a function, and not an array, so you need to send data in as a parameter to the whole function.
  2. R.ascend/R.descend also expect the function as a paremeter, and therefore should not be a parameter of R.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:

  1. The first branch works out whether that's an ascending or descending sort
  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!