I have the following associative list:
myList :: [(myConcept, String)]
myList = [
(myInput, "get_input"),
(myOutput, "get_output"),
(myValues, "get_values")]
-- | Data type
data myConcept = myInput | myOutput | myValues deriving Eq
I want to ensure that the list does not have any conflicting entries if entries are added. How can this be done? Is it possible have the list Map myConcept String to avoid conflicting entries?
Edit:
I can use the following function to prevent conflicting keys, but I would also like to prevent conflicting values.
addOrReplace :: Eq k => k -> v -> [(k, v)] -> [(k, v)]
addOrReplace key value list = (key,value):(filter ((key /=).fst) list)
I understand recursively checking a list, but how do I check the the value from a pair in an associative list?
checkValue :: Eq v => v -> [(k, v)] -> Bool
checkValue :: value [] = False
checkValue :: value [x] = check value of this entry?
checkValue :: value [x:xs]
| check value of this entry?
| otherwise = checkValue value xs
The other issue with the above is that with an [x:xs] it will return the False and not check the rest of the list. How can I add an if condition where "if false, keep checking the list"?
CodePudding user response:
import qualified Data.Map as MAP
import Data.Maybe
import qualified Data.Bimap as BIMAP
data MyConcept = MyInput | MyOutput | MyValues deriving Eq
myList :: [(MyConcept, String)]
myList = [
(MyInput, "get_input"),
(MyOutput, "get_output"),
(MyValues, "get_values")]
To Start of: your fixed checkValue function:
checkValue :: Eq v => v -> [(k, v)] -> Bool
checkValue str [] = False
checkValue str ((t,v):xs)
| str == v = True
| otherwise = checkValue str xs
The generall question you have to ask yourself: should entrys be unique in respective to MyConcept:
(a) schould [(MyInput, "value1"), (MyInput, "value2")] be allowed?
Allso I assume:
(b) MyValues is the only identifier which can hold actual values
-> otherwise it wouldn't make sense to only check on values (= the String part) alone...
Option 1.1: Map assuming (a) isn't allowed:
-- additionally needed:
instance Ord MyConcept where
compare m1 m2
| m1 == m2 = EQ
| otherwise = LT -- not good but practical
type List_Map1 = MAP.Map MyConcept (MyConcept, String)
add1 :: String -> List_Map1 -> List_Map1
add1 str list = if isNothing value
then MAP.insert MyValues (MyValues, str) list
else error "insert here your prefered runtime error message"
where
value = MAP.lookup MyValues list
Option 1.2: Map assuming (a) is allowed:
type List_Map2 = MAP.Map String (MyConcept, String)
add2 :: String -> List_Map2 -> List_Map2
add2 str list = if isNothing value
then MAP.insert str (MyValues, str) list
else error ""
where
value = MAP.lookup str list
Option 2: BiMap
type List_Bimap = BIMAP.Bimap MyConcept (MyConcept, String)
add3 :: String -> List_Bimap -> List_Bimap
add3 str list
| isNothing value = BIMAP.insert MyValues (MyValues, str) list
| str == snd (fromJust value) = error "insert here your prefered runtime error message"
-- Change next line to allow (a)
| otherwise = error "insert here your prefered runtime error message"
where
value :: Maybe (MyConcept, String)
value = BIMAP.lookup MyValues list
finally if you prefere simple lists as outputs:
list1 :: String -> List_Map1 -> [(MyConcept, String)]
list1 s l = map snd $ MAP.toList $ add1 s l
list2 :: String -> List_Map2 -> [(MyConcept, String)]
list2 s l = map snd $ MAP.toList $ add2 s l
list3 :: String -> List_Bimap -> [(MyConcept, String)]
list3 s l = map snd $ BIMAP.toList $ add3 s l
CodePudding user response:
I suspect a Bimap would get you almost everything you want. You might have to write a small wrapper to throw a runtime error (instead of overwriting) on duplicate entries, but it shouldn't be too hard.