Получить восходящий / нисходящий по переменной с помощью ramda - PullRequest
0 голосов
/ 03 мая 2018

У меня есть список пользователей, которых я хочу отсортировать по имени. Список будет выглядеть так:

const data = [
  { id: 2, name: 'Asterios' },
  { id: 1, name: 'Alex' },
  { id: 4, name: 'Tim' },
  { id: 3, name: 'Sadie' },
]

Я реализовал относительно простой селектор, который будет сортировать список пользователей по некоторому свойству.

const getUserList = createSelector(
  getUsers,
  getSortBy,
  getSortOrder,
  (users, sortBy, sortOrder) => R.sortWith([
    R.ascend(R.prop(sortBy)),
  ])(users),
);

Однако я хочу взять переменную sortOrder, которая может быть 'ASC' или 'DESC', и применить этот порядок сортировки.

Я пробовал что-то вроде этого:

const sortDirection = R.ifElse(R.equals('DESC'), descend, ascend);

sortWith([ compose(sortDirection(sortOrder), prop('name')), ])

Есть ли хороший способ применить эту восходящую / нисходящую логику сортировки через переменную?

Ответы [ 2 ]

0 голосов
/ 04 мая 2018

Хорошо, у вас есть несколько мелких проблем. Однако зачастую легче отладить одну большую проблему, чем несколько мелких.

Использование ifElse правильно

Первый - это использование ifElse. Он принимает три функции в качестве параметров и возвращает новую функцию, которая, в зависимости от результата вызова первой с вашими аргументами, вызывает либо вторую, либо третью функцию с этими аргументами. Обратите внимание, что он не возвращает функцию; это называет это. Есть несколько способов исправить это. Вы можете обернуть эти функции с помощью always:

const sortDirection = R.ifElse(R.equals('DESC'), always(descend), always(ascend))

Но я думаю, что проще отказаться от очков и использовать

const sortDirection = dir => (dir === 'DESC' ? descend : ascend)

Понимание compose

Во-вторых, вы передаете немного неправильное значение в ascend или descend. Хотя это не совсем реализация, воспринимайте ascend как что-то вроде

const ascend = curry(fn, a, b) => fn(a) < fn(b) ? -1 : fn(a) > fn(b) ? 1 : 0);

Обратите внимание, что если вы передадите ей унарную функцию, такую ​​как fn = prop('name'), вы получите обратно (a, b) => fn(a) < fn(b) ? -1 : fn(a) > fn(b) ? 1 : 0, которая является функцией двоичного компаратора. sortWith принимает список компараторов. Так что это было бы хорошо.

Но если ваш sortOrder равен 'ASC', то этот

sortWith([ compose(sortDirection(sortOrder), prop('name')), ])

становится

sortWith([ compose(ascend, prop('name')) ])

, что эквивалентно

sortWith([ x => ascend(prop('name')(x)) ])

И функция, переданная сортировщику, не является надлежащим компаратором. Это унарная функция. Проблема в том, что prop('name') - унарная функция, поэтому compose не выполняет то, на что вы надеялись.

Если бы вы немного переставили, вы могли бы получить правильное поведение:

sortWith([ compose(sortDirection(sortOrder), prop)('name'), ], )

это преобразует, сначала с ASC, в

sortWith([ compose(ascend, prop)('name'), ], )

и, следовательно,

sortWith([ (x => ascend(prop(x)))('name'), ], )

что составляет

sortWith([ ascend(prop('name')), ], )

И, как мы видели, ascend(fn) - это двоичный компаратор.

Собираем все вместе

Таким образом, один из способов решить ваши проблемы с помощью

const sortDirection = R.ifElse(R.equals('DESC'), always(descend), always(ascend))
const sortOrder = 'DESC'
sortWith([ compose(sortDirection(sortOrder), prop)('name'), ], data)
//=> [{.. Tim}, {.. Sadie}, {.. Asterios}, {.. Alex}]

И, конечно, если sortOrder = 'ASC', то

sortWith([ compose(sortDirection(sortOrder), prop)('name'), ], data)
//=> [{.. Alex}, {.. Asterios}, {.. Sadie}, {.. Tim}]

Альтернативный подход

Есть две вещи, которые мне все еще не нравятся:

  1. Используется свободная переменная sortOrder. Я бы предпочел, чтобы такие переменные были параметрами моих функций.

  2. Используется sortWith вместо sort. sortWith - это способ объединения функций компаратора. Поскольку у нас есть только один, sort проще.

Вот как я мог бы написать это для решения этих проблем:

const {ascend, descend, prop, sort} = R
const data = [{ id: 2, name: 'Asterios' }, { id: 1, name: 'Alex' }, { id: 4, name: 'Tim' }, { id: 3, name: 'Sadie' }]

const sorter = dir => (dir === 'DESC' ? descend : ascend)(prop('name'))

console.log(sort(sorter('DESC'), data))
//=> [{.. Tim}, {.. Sadie}, {.. Asterios}, {.. Alex}]
console.log(sort(sorter('ASC'), data))
//=> [{.. Alex}, {.. Asterios}, {.. Sadie}, {.. Tim}]
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
0 голосов
/ 03 мая 2018

Это не использует Ramda, но, надеюсь, это даст вам представление о том, как начать работать над проблемой

const ascComparator = (a, b) =>
  a < b
    ? -1
    : a > b
      ? 1
      : 0

const descComparator = (a, b) =>
  ascComparator (b, a)

const data =
  [ { id: 2, name: 'Asterios' }
  , { id: 1, name: 'Alex' }
  , { id: 4, name: 'Tim' }
  , { id: 3, name: 'Sadie' }
  ]

data.sort ((a,b) => ascComparator (a.name, b.name))

console.log (data)
// Alex, Asterios, Sadie, Tim

data.sort ((a,b) => descComparator (a.name, b.name))
  
console.log (data)
// Tim, Sadie, Asterios, Alex

Программа выше демонстрирует красивый вариант использования для contramap

const contramap = (f, g) =>
  (a, b) =>
    f (g (a), g (b))

const prop = k => o =>
  o [k]

const ascComparator = (a, b) =>
  a < b
    ? -1
    : a > b
      ? 1
      : 0

const descComparator = (a, b) =>
  ascComparator (b, a)

const data =
  [ { id: 2, name: 'Asterios' }
  , { id: 1, name: 'Alex' }
  , { id: 4, name: 'Tim' }
  , { id: 3, name: 'Sadie' }
  ]

data.sort (contramap (ascComparator, prop ('name')))

console.log (data)
// Alex, Asterios, Sadie, Tim

data.sort (contramap (descComparator, prop ('name')))
  
console.log (data)
// Tim, Sadie, Asterios, Alex

localeCompare также работает

const ascComparator = (a, b) =>
  a.localeCompare (b)

const descComparator = (a, b) =>
  ascComparator (b, a)
...