您没有指定匹配项的排序方式。一种可能性是按匹配数排序。事实证明,这相当简单。我的第一关是这样的:
const countMatches = (target) => ({tags}) =>
tags .reduce ( (n, tag) => target .includes (tag) ? n + 1 : n, 0)
const descendBy = (fn) => (xs) =>
xs .slice (0)
.sort( (a, b) => fn (b) - fn (a) )
const sortByTagMatches = (target) => descendBy ( countMatches (target) )
sortByTagMatches (tags) (posts) //=> sorted results
countMatches
函数countMatches 只计算目标(tags)与帖子的tags 属性之间的匹配数。在使用完成工作的最具体的代码和更通用、可重用的版本之间总是存在矛盾。但是这里比较通用函数的区别:
const countMatches = (target, name) => (o) =>
o[name] .reduce ( (n, x) => target .includes (x) ? n + 1 : n, 0)
以及具体的:
const countMatches = (target) => ({tags}) =>
tags .reduce ( (n, tag) => target .includes (tag) ? n + 1 : n, 0)
是如此之小——它们之间的使用差异几乎一样小——如果有任何机会我可能想在我的应用程序的其他地方重用这个功能,我会选择这个通用的.
我们可以合并另一个简化:
const countMatches = (target, name) => (o) =>
o[name] .filter ( (x) => target .includes (x) ) .length
这有稍微简单的代码。传递给filter 的函数无疑比传递给reduce 的函数更干净。但是有一个权衡。 filter 创建一个新的帖子数组,仅使用它来获取其length,然后将其丢弃。在热代码中,这可能是一个性能问题,但大多数情况下,当reduce 调用并不比filter 调用复杂得多时,这样做是错误的。
descendBy
descendBy 很简单。您将一个整数返回函数传递给它,它返回一个函数,该函数本身接受一个值数组并返回该数组的排序版本。它不会就地修改数组。如果你真的想要它,你可以删除slice 调用。这个函数是基于我经常使用的一个叫做sortBy的函数,只有减法反转才能下降。我很可能将两者都包含在一个项目中,但如果我这样做了,我可能会将 sortBy 重命名为 ascendBy 以使平行清晰。
如果我们愿意,制作这个函数的更通用版本并不难。除了接受一个返回数字的函数,我们可以有一个接受返回任何有序值、日期、字符串、数字和任何实现valueOf 的函数的函数,本质上是任何可以使用"<" 进行比较的有用的函数。 (这有时称为有序 -- 或 Ord -- 类型)。该版本可能如下所示:
const descendBy = (fn) => (xs) =>
xs .slice (0)
.sort( (a, b, x = fn (a), y = fn (b) ) => x < y ? 1 : x > y ? -1 : 0 )
在这里,我正在考虑是否在第一次通过时更喜欢通用版本。上面的具体一个确实更简单。如果我需要更换它,我可能会使用特定的,因为知道它与通用的兼容。
进一步简化
descendBy 似乎做了太多的工作。它将Ord-returning 函数转换为比较器,然后使用该比较器对列表进行排序。最好将这两个步骤分开,使descendBy 的结果更可重用。这里descend这个名字感觉更合适:
const descend = (fn) =>
(a, b) => fn(b) - fn(a)
const sortByTagMatches = (target) => (xs) =>
xs .slice(0) .sort (descend (countMatches (target, 'tags') ) )
我们将切片和排序转移到主函数中,让descend 变得非常简单。这就是我想我会离开的地方。现在的代码如下所示:
const countMatches = (target, name) => (o) =>
o[name] .filter ( (x) => target .includes (x) ) .length
const descend = (fn) =>
(a, b) => fn(b) - fn(a)
const sortByTagMatches = (target) => (xs) =>
xs .slice(0) .sort (descend (countMatches (target, 'tags') ) )
const tags = ['one', 'two', 'three']
const posts = [{tags: ['four', 'five']}, {tags: ['one', 'six']}, {tags: ['seven']}, {tags: ['one', 'three', 'five']}, {tags: ['nine', 'two']}]
console .log (
sortByTagMatches (tags) (posts)
)
(请注意,我添加了一个带有两个匹配标签的附加帖子来演示附加的排序功能。)
为什么?
user633183 给出了一个很好的答案,解释了为什么我们应该将代码分解成小的可重用函数。这只是演示了相同的过程,但对问题可能如何分解的想法略有不同。