Function switchLists

Alright, so I’m kinda new and this question is what made me make an account on here.
I’ve git a fairly annoying problem and I wish to know of there’s some functionality for this that I dont yet know of.

switchLists :: [[String]] -> [String] -> [[String]]

lets call [[string]] mainlist
lets call [string] targetlist

Mainlist contains n smaller lists of equal length with various strings.
Targetlist contains n strings, and each string in Targetlist (all are different from one another) is the first string of some list inside Mainlist. Now the lists in Mainlist are in any order and I need them ordered so that their first strings are in the same order as they are in Targetlist.

It seems I need to go back and fro, check if theres a match but that isnt really great or easy I think.
targetlist = [“dog”, “cat”, “bird”]

mainlist = [ [“cat”, “lion”, “bobcat”, "lynx], [“bird”, “bo”, “crow”, “kolibri”], [“dog”, “husky”, “wolf”, “chichi”] ]

then mainlist must become
: [ [“dog”, “husky”, “wolf”, “chichi”], [“cat”, “lion”, “bobcat”, "lynx], [“bird”, “bo”, “crow”, “kolibri”] ]

I think the best way is to go over the targetlist and look each element up in the mainlist and combine them in the new order. That could look like this:

switchLists _ [] = []
switchLists mainlist (x:xs) =
  filter (\(y : _) -> x == y) mainlist ++ switchLists mainlist xs

This relies on the assumption that every element of mainlist has at least one element. You could encode that in the types:

switchLists :: [(String, [String])] -> [String] -> [(String, [String])]
switchLists _ [] = []
switchLists mainlist (x:xs) =
  filter (\(y, _) -> x == y) mainlist ++ switchLists mainlist xs

This now guarantees that this function will never produce an error at run time. But now your input needs to be changed slightly to:

mainlist =
  [ ("cat", ["lion", "bobcat", "lynx"])
  , ("bird", ["bo", "crow", "kolibri"])
  , ("dog", ["husky", "wolf", "chichi"])

Another relatively easy solution is to use sortBy, which allows you to sort with a custom ordering function. In this case, you’d lookup the head of the lists in the targetlist and compare their index.

import Data.List

main :: IO ()
main = mapM_ print $ switchLists
  [ ["cat", "lion", "bobcat", "lynx"]
  , ["bird", "bo", "crow", "kolibri"]
  , ["dog", "husky", "wolf", "chichi"]
  ["dog", "cat", "bird"]

switchLists :: [[String]] -> [String] -> [[String]]
switchLists mainlist targetlist = sortBy findTarget mainlist
   findTarget :: [String] -> [String] -> Ordering
   findTarget (a:_) (b:_) = case (a `elemIndex` targetlist, b `elemIndex` targetlist) of
     (Just indexA, Just indexB) -> indexA `compare` indexB
     (_, _)  -> error "list not in target"
   findTarget _ _ = error "list has no first element"

thanks both of you! I’m still really trying to understand the code, however I would say that if I run switchLists [[“cat”, “lion”, “bobcat”, “lynx”], [“bird”, “bo”, “crow”, “kolibri”], [“dog”, “husky”, “wolf”, “chichi”]] [“dog”, “cat”, “bird”]

it gives me 'lexical error at character
why is that?

Your double quotes are weird.

That’s a feature of discourse afaik – the markdown interpreter will turn quotes into opening/closing quotes where it seems appropriate.

@kreta to avoid that, put your code between backticks (or triple backticks for code blocks). The following should be pastable into ghci without issue:
switchLists = [["cat", "lion", "bobcat", "lynx"], ["bird", "bo", "crow", "kolibri"], ["dog", "husky", "wolf", "chichi"]] ["dog", "cat", "bird"]

1 Like