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

use*_*349 10 sorting haskell

我需要按照另一个列表的顺序对一个列表进行排序,但我不知道它是如何完成的.

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

[C, B, G, E]
Run Code Online (Sandbox Code Playgroud)

和一个列表b(设置顺序):

[A, B, C, D, E, F, G, ...]
Run Code Online (Sandbox Code Playgroud)

(例如,这些不是实际值)

然后,列表a应该以与List相同的方式b排序,从而排序为:

[B, C, E, G]
Run Code Online (Sandbox Code Playgroud)

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

bar*_*lle 5

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

 > 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]
Run Code Online (Sandbox Code Playgroud)

这段代码应该可以工作:

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..])
Run Code Online (Sandbox Code Playgroud)

编辑:更有效的解决方案是使用sortOn,如下所示:

sortWithOrder order = sortOn getOrder
Run Code Online (Sandbox Code Playgroud)

  • 在最近的“base”版本中,有一个“sortOn”函数可以避免在映射中一遍又一遍地查找相同的键。对于旧版本,您可以复制其装饰-排序-取消装饰逻辑。 (2认同)

גלע*_*רקן 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"]
Run Code Online (Sandbox Code Playgroud)

因此我们可以将这个函数定义为

sortAlong :: Eq b => [b] -> [b] -> [b]
sortAlong order = map snd . sortBy (comparing fst) . map (\x -> (lookup x z, x))
    where
    z = zip order [0..]
Run Code Online (Sandbox Code Playgroud)

约束Ord允许更有效的路线通过Map,但此版本只需要Eq

> sortAlong "ABCDEFG" "CBGE"
"BCEG"
Run Code Online (Sandbox Code Playgroud)