Ruby-like squish in Haskell squish is an onomatopoeic string function provided by Ruby on Rails that works on strings.

Given a string, squish condenses all the consecutive spaces into one.1 (Imagine that you take a string at both ends and compress it with both hands, spaces will be merged and you will hear the typical squish sound).

I went I little overboard and wrote three variations of this function:


squishAll that will squish together all repeated chars:
squishAll "aaabbbccc" -&gt; "abc"
squishOn that will squish together any specified char:
squishOn 'a' "aaabbbccc" -&gt; "abbbccc"
squish for spaces only: squish "foo   bar   baz" -&gt; "foo bar baz"




import Data.List
import Data.Foldable

squishAll :: Eq a =&gt; [a] -&gt; [a]
squishAll = (map head) . group

squishOn :: Eq a =&gt; a -&gt; [a] -&gt; [a]
squishOn item = Data.List.concat . (map firstIfEqualElseAll) . group 
  where firstIfEqualElseAll (x:xs) = if x == item then [x] else (x:xs)

squish = squishOn ' '

main = print $ [squishAll "aaabbbccc", squishOn 'a' "aaabbbccc", squish "foo   bar   baz"]




1 The Ruby function that this is modelled on would also remove any spaces at the start and at the end.  I've chosen not to implement this.
I have no complaints about this.

Esthetically it seems that squishOn 'a' "aaabcbcbc" should create the groups:

[ "aaa", "bcbcbc" ]


instead of:

[ "aaa", "b", "c", "b", "c", "b", "c" ]


Due to laziness it may not make any difference. This is something that perhaps should be benchmarked. To form the groups the first way use the groupBy function from Data.List like this:

groupBy (\x y -&gt; x == y || (x /= 'a' &amp;&amp; y /= 'a')) input


So, two characters are in the same group if they are equal or if they are not both the character to be squished. Of course, if you do this you'll have to switch the groups back together differently with something like:

concatMap f (groupBy (\x y -&gt; ...) ...)
  where f ('a':xs) = "a"
        f xs       = xs


(Substitute the literal 'a' with the squishing character.)

This alternate approach probably makes more sense if you were to define squishing on ByteString or Text.