按照另一个列表的顺序对一个列表进行排序

11

我需要按照另一个列表的顺序对一个列表进行排序,但我不知道应该如何实现。

例如:我可能有一个类似于以下列表的列表a

[C, B, G, E]

还有一个列出项目的列表 b (可以设置顺序):

[A, B, C, D, E, F, G, ...]

仅作为示例,这些不是实际的值。

那么,列表 a 应该按照与列表 b 相同的方式进行排序,因此变成以下排序:

[B, C, E, G]

如何按照另一个列表的顺序进行排序?

2个回答

5

如果我理解正确,其中一个列表给出了另一个列表中所有元素的相对顺序。例如:

 > sortWithOrder [5,1,2,3,4] [1,2,3,4,5,5,4,3,2,1]
 [5,5,1,1,2,2,3,3,4,4]

这段代码应该可以运行:
module SortWithOrder where

import qualified Data.Map.Strict as M
import Data.List
import Data.Ord

sortWithOrder :: Ord a
              => [a] -- order list
              -> [a] -- source list
              -> [a]
sortWithOrder order = sortBy (comparing getOrder)
    where
        getOrder k = M.findWithDefault (-1) k ordermap
        ordermap = M.fromList (zip order [0..])

编辑:更高效的解决方案是使用 sortOn,如下所示:
sortWithOrder order = sortOn getOrder

2
在最近的 base 版本中,有一个 sortOn 函数可以避免反复查找映射中相同的键。对于旧版本,您可以复制其装饰-排序-取消装饰逻辑。 - dfeuer
啊,我从来没想到它是这样工作的!我会更新我的答案。 - bartavelle
问题仅接受带有Eq约束的类型,不一定是Ord - Will Ness

5
您可以将订单映射到列表并进行排序:
Prelude> let order = zip ["A", "B", "C", "D", "E", "F", "G"] [0..]

Prelude> let myList = ["C", "B", "G", "E"]

Prelude> import Data.List (sort)

Prelude> map snd . sort . map (\x -> (lookup x order, x)) $ myList

["B","C","E","G"]

因此,我们可以将此函数定义为:
sortAlong :: Eq b => [b] -> [b] -> [b]
sortAlong order = map snd . sortBy (comparing fst) . map (\x -> (lookup x z, x))
    where
    z = zip order [0..]

Ord 约束可以通过 Map 实现更高效的路线,但这个版本只需要 Eq

> sortAlong "ABCDEFG" "CBGE"
"BCEG"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接