It so happened that Hackage has a hashmap package! I am going to create a small data type based on this HashMap, which I will call MultiMap. This is a typical trick: it's just a hash map of linked lists. I'm not sure what the correct name for MultiMap really is.
import qualified Data.HashMap as HM import Data.Hashable import Prelude hiding (lookup) type MultiMap kv = HM.Map k [v] insert :: (Hashable k, Ord k) => k -> a -> MultiMap ka -> MultiMap ka insert kv = HM.insertWith (++) k [v] lookup :: (Hashable k, Ord k) => k -> MultiMap ka -> [a] lookup km = case HM.lookup km of Nothing -> [] Just xs -> xs empty :: MultiMap ka empty = HM.empty fromList :: (Hashable k, Ord k) => [(k,v)] -> MultiMap kv fromList = foldr (uncurry insert) empty
I imitated only the fundamentals of the Map: insert, search, empty and from the list. Now it's pretty easy to turn entries into a MutliMap :
data Entry = Entry {wrd, def :: String, len :: Int, phr :: Bool} deriving (Show) icards = [("the", "le"),("savage", "violent"),("work", "travail"), ("wild", "sauvage"),("chance", "occasion"),("than a", "qu'un")] entries :: [Entry] entries = map (\(x, y) -> Entry xy (length x) (' ' `elem` x)) icards fromEntryList :: [Entry] -> MutiMap Int Entry fromEntryList es = fromList $ map (\e -> (len e, e)) es
By loading this into ghci, we can now find a list of records with a given length:
ghci> let m = fromEntryList entries ghci> lookup 3 m [Entry {wrd = "the", def = "le", len = 3, phr = False}] ghci> lookup 4 m [Entry {wrd = "work", def = "travail", len = 4, phr = False}, Entry {wrd = "wild", def = "sauvage", len = 4, phr = False}]
(Note that this lookup not the one defined in Prelude.) Similarly, you can use the English word as a key.
-- import Data.List (find) -- up with other imports fromEntryList' :: [Entry] -> MultiMap String Entry fromEntryList' es = fromList $ map (\e -> (wrd e, e)) es eLookup :: String -> MultiMap String Entry -> Maybe Entry eLookup str m = case lookup str m of [] -> Nothing xs -> find (\e -> wrd e == str) xs
Testing...
ghci> let m = fromEntryList' entries ghci> eLookup "the" m Just (Entry {wrd = "the", def = "le", len = 3, phr = False}) ghci> eLookup "foo" m Nothing
Please note that in eLookup we first search the map to determine if anything was placed in this slot. Since we use a hash set, we need to remember that two different lines can have the same hash code. Therefore, if the slot is not empty, we run find in the linked list to see if any of the entries really matches the correct English word. If you are interested in performance, you should use Data.Text instead of String .