Reputation: 378
I'm trying to create a function that drops every n'th element from a string.
dropEvery :: String -> Int -> String
dropEvery str n = map (\(char, indx) -> if indx `mod` n /= 0 then char else ' ') (zip str [1..])
Right now it simply replaces every n'th element with a space, but what am I supposed to put after the "else" if I want it to return an "empty char". I understand that such a thing doesn't exist in Haskell so the question is - how am I supposed to tell Haskell to not return anything and just move on to the next char?
Upvotes: 6
Views: 2522
Reputation: 48591
You can do this without mod
. I'm going to flip the order of the arguments to make this more idiomatic.
dropEvery :: Int -> [a] -> [a]
dropEvery n xs = map fst . filter ((/= n) . snd) $ zip xs (cycle [1..n])
If speed is critical, it would likely be most efficient to use this technique with explicit recursion or foldr
. Something like this:
dropEvery n xs = foldr go (`seq` []) xs n where
go _ r 1 = r n
go x r k = x : r (k - 1)
Upvotes: 3
Reputation: 54058
You can't do this with just map
, by definition it can't change the length of the collection it's applied to. However, you can get this to work without too many changes by switching to concatMap
. This function requires the your function that you're mapping return a list, then it concatenates all the results together. All you'd need to do is
dropEvery str n =
concatMap (\(char, indx) -> if indx `mod` n /= 0 then [char] else []) (zip str [1..])
Upvotes: 7
Reputation: 144136
map
preserves the structure of the list, while your operations modifies it by removing elements. This means you can't use map
, but you can use mapMaybe
which allows you to provide a function which returns Nothing
for elements you want to remove from the output:
import Data.Maybe (mapMaybe)
dropEvery str n = mapMaybe (\(char, indx) -> if indx `mod` n /= 0 then Just(char) else Nothing) (zip str [1..])
Upvotes: 5