通过ramda变量升序/降序

时间:2018-05-03 18:15:48

标签: javascript functional-programming ramda.js

我有一个我想按名称排序的用户列表。列表看起来像这样:

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 :(得分:4)

好的,这里有几个小问题。但是,调试一个大问题通常比几个小问题更容易。

正确使用ifElse

第一个是使用ifElse。这需要三个函数作为参数,并返回一个新函数,根据使用您的参数调用第一个函数的结果,调用使用这些参数的第二个或第三个函数。请注意,它不会返回该功能;它称之为。有几种方法可以解决这个问题。您可以使用always包装这些函数:

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

但我认为放弃无点并使用

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

了解compose

其次,您将稍微错误的值传递给ascenddescend。虽然这不是一个完整的实现,但请将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而不是sortsortWith是一种组合比较器功能的方法。由于我们只有一个,sort更简单。

  3. 以下是我如何编写它来解决这些问题:

    &#13;
    &#13;
    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}]
    &#13;
    <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
    &#13;
    &#13;
    &#13;

答案 1 :(得分:2)

这不使用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)