使用 ramda 排序
Sort with ramda
我现在需要按名称对对象数组进行排序。
Array = [
{id: 3d4, name: A2},
{id: 7b2, name: A1+ Beta},
{id: 9h5, name: A2 Beta},
{id: 1x1, name: A1+}
]
如何使用 Ramda 按此顺序排序:
- A1+
- A2
- A1+ 测试版
- A2 测试版
我已经考虑过按名称长度拆分然后排序,但我一直坚持下去。有人可以帮我解决这个问题吗?
在 Ramda 的 documentation 上找到,非常简单,您可以使用以下内容创建过滤函数:
const sortByNameCaseInsensitive = R.sortBy(R.compose(R.toLower, R.prop('name')));
然后你在里面应用你的数组sortByNameCaseInsensitive(arr);
您可以尝试在 new RegExp('[+]?\s')
拆分数组后反转 R.reverse
数组。
const array = [
{id: '3d4', name: 'A2'},
{id: '9hh', name: 'A3 Beta'},
{id: '7b2', name: 'A1+ Beta'},
{id: '9h5', name: 'A2 Beta'},
{id: '1x1', name: 'A1+'},
{id: '1x2', name: 'A3+'}
];
// pick name and convert to lowercase if required.
const namePropCompose = R.compose(R.toLower, R.prop('name'));
// split by '+ ' or ' ' and reverse the array.
const splitNameAndReverse = R.compose(R.reverse, R.split(new RegExp('[+]?\s')));
// finally sort with reversed array
const sortByNameCaseInsensitive = R.sortBy(R.compose(splitNameAndReverse, namePropCompose));
const result = sortByNameCaseInsensitive(array);
console.log(JSON.stringify(result))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>
为了更好的可读性,我将它们全部分成了不同的组合。 Regex and toLower
在这里可能用处不大,总之,如果你愿意,你可以在一行中完成所有这些。
sortBy(compose(reverse, split(' '), prop('name')))
我认为 ['A1+', 'A2', 'A1+ Beta', 'A2 Beta']
排名非常随意,可能建议手动排序它们的优先级。
- 找到
scores
数组中每个 rank
的 position
- 减去他们的分数为
a - b
const scores = ['A1+', 'A2', 'A1+ Beta', 'A2 Beta'];
const findScorePriority = R.pipe(
R.prop('name'),
R.equals,
R.findIndex(R.__, scores),
);
const byPriority = R.useWith(
R.subtract,
[findScorePriority, findScorePriority],
);
const data = [
{ id: '3d4', name: 'A2' },
{ id: '7b2', name: 'A1+ Beta' },
{ id: '9h5', name: 'A2 Beta' },
{ id: '1x1', name: 'A1+' },
];
console.log(
R.sort(byPriority, data),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous"></script>
从您的示例中还不够清楚您的排序顺序是基于简单的固定列表还是您没有提供足够的示例来说明一般原则。如果是前者,那么该技术的一些变体 就是您想要的。
此处我们假设您希望按 name
字段的前半部分('A1+'
、'A2'
等)进行主要排序,并按后半部分(例如 'Beta'
) 如果存在的话。
我们可以用 vanilla JS 很容易地写这个,像这样:
const customSort = (array) =>
[...array] .sort ((
{name: a}, {name: b},
[a0, a1 = ''] = a.split(' '), [b0, b1 = ''] = b.split(' ')
) =>
a0 < b0 ? -1 : a0 > b0 ? 1 : a1 < b1 ? -1 : a1 > b1 ? 1 : 0
)
Ramda 提供了本机 Array.prototype.sort
没有的两件事:首先,它使用 函数 而不是 方法 ,并且因此更容易与其他功能组合。其次,它是非破坏性的。 Ramda 排序函数不会就地改变数组,而是 return 它的排序副本。
Ramda 提供了三个主要函数来处理排序。首先是sort
,类似这样:
const sort = (comp) => (array) =>
[... array] .sort (comp)
然后是 sortBy
,它将一个函数从数组中的元素转换为某种有序类型(String
、Number
、Date
、{valueOf}
) 并根据该函数的结果对数组进行升序排序。
还有 sortWith
,这很像 sortBy
,但是需要一个 array 这样的函数,所以如果前两个并列根据第一个功能,然后我们用第二个功能测试,依此类推。
有三个辅助函数,descend
和它稍微多余的双胞胎 ascend
,它们采用相同类型的函数传递给 sortBy
或 sortWith
和 return 是适合传递给 sort
或 Array.prototype.sort
的函数。我们可以这样使用它们:
const oldestFirst = sort (descend (prop ('age')))
// ...
const reordered = oldestFirst (people)
或喜欢
people .sort (descend (prop('age')) // WARNING: mutates `people`
并且我们有 comparator
,它将报告第一项是否小于第二项的二元谓词转换为再次适用于 sort
或 Array.prototype.sort
的比较函数。
由于我们需要对此进行二级排序,因此 sortWith
是合适的。我们可以像这样使用 ascend
助手来编写它:
const customSort = sortWith ([
ascend (({name}) => name .split (' ') [0]),
ascend (({name}) => name .split (' ') [1] || ''),
])
虽然我对此很满意,但有些人更喜欢完全无积分,在这种情况下并不太难:
const customSort = sortWith ([
ascend (pipe (prop ('name'), split (' '), prop(0))),
ascend (pipe (prop ('name'), split (' '), propOr('', 1)))
])
在我看来,这些方法比原始版本更具可读性。我看到他们唯一的缺点是每次比较都需要调用 split
两次,而普通版本只需调用一次。因此,由于额外的函数调用,它们的性能肯定会低于普通版本,但我希望它们足够快以用于许多用途。
您可以在这段代码中看到所有这些选项:
const customSort1 = (array) =>
[...array] .sort ((
{name: a}, {name: b},
[a0, a1 = ''] = a.split(' '), [b0, b1 = ''] = b.split(' ')
) =>
a0 < b0 ? -1 : a0 > b0 ? 1 : a1 < b1 ? -1 : a1 > b1 ? 1 : 0
)
const customSort2 = sortWith ([
ascend (({name}) => name .split (' ') [0]),
ascend (({name}) => name .split (' ') [1] || ''),
])
const customSort3 = sortWith ([
ascend (pipe (prop ('name'), split (' '), prop(0))),
ascend (pipe (prop ('name'), split (' '), propOr('', 1)))
])
const array = [{id: '3d4', name: 'A2'}, {id: '9hh', name: 'A3 Beta'}, {id: '7b2', name: 'A1+ Beta'}, {id: '9h5', name: 'A2 Beta'}, {id: '5h4', name: 'A1+ Alpha'}, {id: '1x1', name: 'A1+'}, {id: '1x2', name: 'A2 Alpha'}, {id: '1x3', name: 'A3+'}]
console .log (customSort1 (array))
console .log (customSort2 (array))
console .log (customSort3 (array))
.as-console-wrapper {min-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js"></script>
<script> const {sortWith, split, ascend, pipe, prop, propOr} = R </script>
我现在需要按名称对对象数组进行排序。
Array = [
{id: 3d4, name: A2},
{id: 7b2, name: A1+ Beta},
{id: 9h5, name: A2 Beta},
{id: 1x1, name: A1+}
]
如何使用 Ramda 按此顺序排序:
- A1+
- A2
- A1+ 测试版
- A2 测试版
我已经考虑过按名称长度拆分然后排序,但我一直坚持下去。有人可以帮我解决这个问题吗?
在 Ramda 的 documentation 上找到,非常简单,您可以使用以下内容创建过滤函数:
const sortByNameCaseInsensitive = R.sortBy(R.compose(R.toLower, R.prop('name')));
然后你在里面应用你的数组sortByNameCaseInsensitive(arr);
您可以尝试在 new RegExp('[+]?\s')
拆分数组后反转 R.reverse
数组。
const array = [
{id: '3d4', name: 'A2'},
{id: '9hh', name: 'A3 Beta'},
{id: '7b2', name: 'A1+ Beta'},
{id: '9h5', name: 'A2 Beta'},
{id: '1x1', name: 'A1+'},
{id: '1x2', name: 'A3+'}
];
// pick name and convert to lowercase if required.
const namePropCompose = R.compose(R.toLower, R.prop('name'));
// split by '+ ' or ' ' and reverse the array.
const splitNameAndReverse = R.compose(R.reverse, R.split(new RegExp('[+]?\s')));
// finally sort with reversed array
const sortByNameCaseInsensitive = R.sortBy(R.compose(splitNameAndReverse, namePropCompose));
const result = sortByNameCaseInsensitive(array);
console.log(JSON.stringify(result))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>
为了更好的可读性,我将它们全部分成了不同的组合。 Regex and toLower
在这里可能用处不大,总之,如果你愿意,你可以在一行中完成所有这些。
sortBy(compose(reverse, split(' '), prop('name')))
我认为 ['A1+', 'A2', 'A1+ Beta', 'A2 Beta']
排名非常随意,可能建议手动排序它们的优先级。
- 找到
scores
数组中每个rank
的position
- 减去他们的分数为
a - b
const scores = ['A1+', 'A2', 'A1+ Beta', 'A2 Beta'];
const findScorePriority = R.pipe(
R.prop('name'),
R.equals,
R.findIndex(R.__, scores),
);
const byPriority = R.useWith(
R.subtract,
[findScorePriority, findScorePriority],
);
const data = [
{ id: '3d4', name: 'A2' },
{ id: '7b2', name: 'A1+ Beta' },
{ id: '9h5', name: 'A2 Beta' },
{ id: '1x1', name: 'A1+' },
];
console.log(
R.sort(byPriority, data),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous"></script>
从您的示例中还不够清楚您的排序顺序是基于简单的固定列表还是您没有提供足够的示例来说明一般原则。如果是前者,那么该技术的一些变体
此处我们假设您希望按 name
字段的前半部分('A1+'
、'A2'
等)进行主要排序,并按后半部分(例如 'Beta'
) 如果存在的话。
我们可以用 vanilla JS 很容易地写这个,像这样:
const customSort = (array) =>
[...array] .sort ((
{name: a}, {name: b},
[a0, a1 = ''] = a.split(' '), [b0, b1 = ''] = b.split(' ')
) =>
a0 < b0 ? -1 : a0 > b0 ? 1 : a1 < b1 ? -1 : a1 > b1 ? 1 : 0
)
Ramda 提供了本机 Array.prototype.sort
没有的两件事:首先,它使用 函数 而不是 方法 ,并且因此更容易与其他功能组合。其次,它是非破坏性的。 Ramda 排序函数不会就地改变数组,而是 return 它的排序副本。
Ramda 提供了三个主要函数来处理排序。首先是sort
,类似这样:
const sort = (comp) => (array) =>
[... array] .sort (comp)
然后是 sortBy
,它将一个函数从数组中的元素转换为某种有序类型(String
、Number
、Date
、{valueOf}
) 并根据该函数的结果对数组进行升序排序。
还有 sortWith
,这很像 sortBy
,但是需要一个 array 这样的函数,所以如果前两个并列根据第一个功能,然后我们用第二个功能测试,依此类推。
有三个辅助函数,descend
和它稍微多余的双胞胎 ascend
,它们采用相同类型的函数传递给 sortBy
或 sortWith
和 return 是适合传递给 sort
或 Array.prototype.sort
的函数。我们可以这样使用它们:
const oldestFirst = sort (descend (prop ('age')))
// ...
const reordered = oldestFirst (people)
或喜欢
people .sort (descend (prop('age')) // WARNING: mutates `people`
并且我们有 comparator
,它将报告第一项是否小于第二项的二元谓词转换为再次适用于 sort
或 Array.prototype.sort
的比较函数。
由于我们需要对此进行二级排序,因此 sortWith
是合适的。我们可以像这样使用 ascend
助手来编写它:
const customSort = sortWith ([
ascend (({name}) => name .split (' ') [0]),
ascend (({name}) => name .split (' ') [1] || ''),
])
虽然我对此很满意,但有些人更喜欢完全无积分,在这种情况下并不太难:
const customSort = sortWith ([
ascend (pipe (prop ('name'), split (' '), prop(0))),
ascend (pipe (prop ('name'), split (' '), propOr('', 1)))
])
在我看来,这些方法比原始版本更具可读性。我看到他们唯一的缺点是每次比较都需要调用 split
两次,而普通版本只需调用一次。因此,由于额外的函数调用,它们的性能肯定会低于普通版本,但我希望它们足够快以用于许多用途。
您可以在这段代码中看到所有这些选项:
const customSort1 = (array) =>
[...array] .sort ((
{name: a}, {name: b},
[a0, a1 = ''] = a.split(' '), [b0, b1 = ''] = b.split(' ')
) =>
a0 < b0 ? -1 : a0 > b0 ? 1 : a1 < b1 ? -1 : a1 > b1 ? 1 : 0
)
const customSort2 = sortWith ([
ascend (({name}) => name .split (' ') [0]),
ascend (({name}) => name .split (' ') [1] || ''),
])
const customSort3 = sortWith ([
ascend (pipe (prop ('name'), split (' '), prop(0))),
ascend (pipe (prop ('name'), split (' '), propOr('', 1)))
])
const array = [{id: '3d4', name: 'A2'}, {id: '9hh', name: 'A3 Beta'}, {id: '7b2', name: 'A1+ Beta'}, {id: '9h5', name: 'A2 Beta'}, {id: '5h4', name: 'A1+ Alpha'}, {id: '1x1', name: 'A1+'}, {id: '1x2', name: 'A2 Alpha'}, {id: '1x3', name: 'A3+'}]
console .log (customSort1 (array))
console .log (customSort2 (array))
console .log (customSort3 (array))
.as-console-wrapper {min-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js"></script>
<script> const {sortWith, split, ascend, pipe, prop, propOr} = R </script>