I have a list of lists in single chars like: [["a"],["b"],["c"],["d"]],
and I have a map for example [("a", "A"), ("b", "B")], I would like to find elements in list that match the map keys and replace the list value with the map value for that key and remove all of the remaining unchanged single chars.
So for example from the above, if I have list of [["a"],["b"],["c"],["d"]] and map of [("a", "A"), ("b", "B")] I want to get back a single list like this: ["A", "B"]
As I am a total noob with Haskell so any help will be appreciated :)
You can combine lookup with catMaybes:
import Data.Maybe
list :: [[String]]
list = [["a"],["b"],["c"],["d"]]
replacements :: [(String, String)]
replacements = [("a", "A"), ("b", "B")]
replaced :: [String]
replaced = catMaybes . map (\x -> lookup x replacements) . concat $ list
main :: IO ()
main = print replaced -- ["A", "B"]
Related
I am new haskell. I want always check the first element from the string and compare the string with Char value from the list. So firstly I will compare "H" with 'E', then "H" with 'F', then "H" with 'G', and last "H" with 'H'. And if "H" = 'H' so I want add to my output string value of list element, in my case for 'H' it is "GG". But If all four are not equal so I want add to list current character in my case "A".
So I want my output to look like this ["GG","A","GG","A"]
Do you know some solution ?
My code:
findc :: String -> [(Char, String)] -> [String]
findc str list =
let filtered = [ if inputChar `elem` [(str !! num)] then decodedValue else if inputChar /= (str !! num) then [(str !! num)] else [] | num<-[0..(length str)-1],(inputChar,decodedValue) <- list]
in filtered
input:
findc "HAHA" [('E', "AB"), ('F', "CD"), ('G', "EF"), ('H', "GG")]
bad output:
["H","H","H","GG","A","A","A","A","H","H","H","GG","A","A","A","A"]
Your function is using a list comprehension with effectively nested loops. It loops over the indices of the string and then over the elements in the list of tuples you've passed in. Both contain 4 elements, so it's natural that your list comprehension iterates 16 times and you end up with 16 elements in your incorrect output.
A simpler, correct approach
[(Char, String)] describes a lookup table.
Fortunately, Haskell gives us the lookup function.
:ghci> :t lookup
lookup :: Eq a => a -> [(a, b)] -> Maybe b
ghci> tbl = [('E', "AB"), ('F', "CD"), ('G', "EF"), ('H', "GG")]
ghci> lookup 'H' tbl
Just "GG"
ghci> lookup 'A' tbl
Nothing
Now, you just need to map this to your string in a list comprehension, handling the Just case differently from the Nothing case.
ghci> [case lookup ch tbl of {Just s -> ...; Nothing -> ...} | ch <- "HAHA"]
["GG","A","GG","A"]
Using Data.Maybe.maybe
Unsurprisingly the Data.Maybe library has fucntions for dealing with Maybe values like those returned by lookup.
case expr1 of { Just x -> expr2; Nothing -> expr3 }
Can be expressed as:
maybe expr3 (\x -> expr2) expr1
So we can write:
ghci> [maybe ... ... $ lookup ch tbl | ch <- "HAHA"]
["GG","A","GG","A"]
Trying to work out a solution to this problem..
Let's say I have x = fromList[("a", 1), ("b", 2), ("c", 3)]
And have a list ["a", "a", "b", "c" , "c"]
How would I think about producing something like..
Map.fromList[("a", 2), ("b", 2), ("c", 6)]
Any help leading in the right direction would be greatly appreciated
I'm starting from the assumption in K. A. Buhr's comment that you want to obtain the product of the counts in the original hash and the hash originating from the input list.
For this we need a function occurrences which transforms a list of strings into a hash from string to number of occurrences. This can easily be done using the insertWith function, which combines a the value to be inserted with the stored values using the given operation if a stored value exists and otherwise just inserts.
To multiply the occurrences with the existing hash, we use the unionWith function which works in a similar way as insertWith in the sense that it forms the union using a given operation if the element exists in both hashes and otherwise just inserts.
{-# OPTIONS_GHC -Wall #-}
import Data.Map
x :: Map String Int
x = fromList [("a", 1), ("b", 2), ("c", 3)]
occurrences :: Ord k => [k] -> Map k Int
occurrences l = aux l empty
where
aux [] h = h
aux (a:as) h = aux as $ insertWith (+) a 1 h
main :: IO ()
main = print $ unionWith (*) x $ occurrences ["a", "a", "b", "c", "c"]
Live example on Wandbox
I'm trying to get this list of tuples:
[(2,"a"), (1,"a"), (1,"b"), (1,"c"), (2,"dd")]
into this string output
a 1,2
b 1
c 1
dd 2
I assume I need to use the unzip and unlines functions. But I also saw some solutions using the show function which makes the integers strings. Any ideas?
Break the problem down into steps. What you really want to do first is aggregate all the tuples that have the same string in the second position, so you'll have a function like
aggregate :: [(Int, String)] -> [([Int], String)]
So for your input list you would get the output
[([1, 2], "a"), ([1], "b"), ([1], "c"), ([2], "dd")]
Your hints are
aggregate items = someFunc (map (\(num, str) -> ([num], str)) items)
And take a look at foldr. Before you ask a follow up question about foldr, there are probably hundreds of stackoverflow answers showing how to use it already, take some time to figure it out or it'll get closed immediately as a duplicate.
Then you need a function to convert a single tuple of this form into a single String for outputting:
prettyPrint :: ([Int], String) -> String
prettyPrint (nums, str) = str ++ " " ++ joinWithComma (map show nums)
Where you'll have to implement joinWithComma yourself. Then you need to calculate this and print it for each item in your aggregated list, mapM_ and putStrLn would be preferred, so your main might look like
main :: IO ()
main = do
let inputList = [(2,"a"), (1,"a"), (1,"b"), (1,"c"), (2,"dd")]
mapM_ (putStrLn . prettyPrint) (aggregate inputList)
If you have this list:
pairs = [ ("a", [1,2]), ("b", [1]), ("c", [1]), ("dd", [2]) ]
then you can get the desired output with:
putStrLn $ unlines [ x ++ " " ++ unwords (map show ys) | (x, ys) <- pairs ]
but you have to figure out how to get the pairs list first.
Work step by step. You could start with the groupBy function:
groupBy (\x y-> (snd x)==(snd y)) [(2,"a"), (1,"a"), (1,"b"), (1,"c"), (2,"dd")]
gives you
[[(2,"a"),(1,"a")],[(1,"b")],[(1,"c")],[(2,"dd")]]
The next step would be "totalling" the inner lists, map and foldL (and depending on your requirements maybe sortBy) should be helpful. If you have this, constructing the output is trivial (using show, as you already mentioned).
Is there a function in Haskell where say if you supplied a Char, and a List of 13 Pairs of Chars (all different i.e. every letter of the alphabet was used once and only once) it would return you the Char which is paired with your inputted Char i.e. if I inputted the following:
pairedChar Q [(A,Z),(B,Y),(C,X),(D,W),(E,V),(F,U),(G,T),(H,S),(I,R),(J,Q),(K,P),(L,O),(M,N)]
I would like it to return J?
If there isn't a function like that I was thinking maybe of doing it with unzip to get a pair of lists but wasn't sure what to do with the lists after I get them?
The lookup function does this; not just for the kind of pairs you describe but for any list of two-element tuples. A list of such tuples is known as an association list, by the way.
It returns a Maybe, because there might beono match.
lookup :: Eq a => a -> [(a, b)] -> Maybe b
lookup key assocs
looks up a key in an association list
This answer builds on itsbruce's answer by still using lookup, but also first massaging the input list to include each pair twice, once for each ordering of elements.
Let's assume that your list is called pairs:
pairs :: [(Char, Char)]
pairs = [('A', 'Z'), ('B', 'Y'), ..., ('M', 'N')]
Then all you need to do is duplicate each pair and swap the elements:
import Data.Tuple (swap)
allPairs :: [(Char, Char)]
allPairs = pairs ++ map swap pairs
-- allPairs = [('A', 'Z') ... ('M', 'N'), ('Z', 'A'), ... ('N', 'M')]
... where swap is a function from Data.Tuple that takes the two elements of a tuple and swaps them. It's defined like this:
swap :: (a, b) -> (b, a)
swap (x, y) = (y, x)
Now you can do a lookup on the allPairs list:
pairedChar :: Char -> Maybe Char
pairedChar c = lookup c allPairs
If you want each duplicate pair to be adjacent to each other in the list, then you can also write allPairs like this:
allPairs = do
(x, y) <- pairs
[(x, y), (y, x)]
Now it will contain this ordering:
allPairs = [('A', Z'), ('Z', 'A'), ('B', 'Y'), ('Y', 'B') ... ('M', 'N'), ('N', 'M')]
You could just make a longer list.
alphabet = "ABCDEFGHIJKLMNOPQRTSUVXYZ"
pairs = zip alphabet (reverse alphabet)
theOtherChar k = lookup k pairs --does the job for you now.
I have a large nested vector that look like this:
import Data.Vector
let x = fromList [["a", "b", "12", "d"], ["e", "f", "34", "g"]...]
I would like to convert the strings to integers at position 2 in each nested list I was trying to do this with map and a comprehension like this:
let y = Data.Vector.map (\a -> read a :: Int) [i !! 2 | i <- x]
What am I doing wrong? I would like the output to be:
(("a", "b", 12, "d"), ("e", "f", 34, "g")...)
There are a number of problems here.
First of all, the result of a list comprehension is a list, so you're calling Data.Vector.map on a list, which won't work. And the x inside the comprehension is a Vector, which is another type mismatch. Either use a list instead of a Vector (along with Prelude.map) or convert the list to a Vector (in which case you can't use a list comprehension).
Secondly, ignoring the list/Vector problem, [i !! 2 | i <- x] will give you a list containing only the elements at position 2 from each sub-list. Using your example, the comprehension would yield ["12", "34"]. Then when you map read over it, you'll get [12, 34], rather than the output you're shooting for.
Finally, the output you're wanting to see is not valid for lists or for Vectors in Haskell. Both types of container must be homogeneous, i.e. they cannot contain values of more than one type. A [Int] cannot contain Strings, nor can a [String] contain Ints, but your desired output contains both. There are ways you can get around this using existential types, but chances are there's a better solution for your underlying problem than to try to build heterogeneous collections.
Edit: You edited the last part of your post to use tuples, so the above paragraph no longer applies. The first two problems I mentioned still exist, though.
If you start with a list of 4-tuples ([(String, String, String, String)]), you can get what you want like this:
> let x = [("a", "b", "12", "d"), ("e", "f", "34", "g")]
> map (\(a, b, c, d) -> (a, b, read c :: Int, d)) x
[("a", "b", 12, "d"), ("e", "f", 34, "g")]
It looks much like you should use a more sophisticated data type than a 4-tuple, like
data YourType_StringNum = YourType_StringNum { ytsnLetters1 :: String
, ytsnLetters2 :: String
, ytsnNumber :: String
, ytsnLetters3 :: String }
data YourType_IntNum = YourType_IntNum { ytinLetters1 :: String
, ytinLetters2 :: String
, ytinNumber :: Int
, ytinLetters3 :: String }
(of course with better identifiers). Then define a function like
toYtin :: YourType_StringNum -> YourType_IntNum
toYtin(YourType_StringNum a b s c) = YourType_IntNum a b (read s) c
With that, your problem reduces to transforming a Vector YourType_StringNum to a Vector YourType_IntNum, and that's trivially done with Data.Vector.map toYtin.