154

Is there a function to concatenate elements of a list with a separator? For example:

> foobar " " ["is","there","such","a","function","?"]
["is there such a function ?"]

Thanks for any reply!

Matvey Aksenov
  • 3,802
  • 3
  • 23
  • 45
Fopa Léon Constantin
  • 11,863
  • 8
  • 48
  • 82
  • 17
    I know lmgtfy answers are bad, but it's worth noting that a search for "String -> [String] -> String" on hoogle gets just what you want. http://www.haskell.org/hoogle/ – sigfpe Feb 10 '12 at 00:24
  • 4
    for joining with spaces you also have `unwords` – epsilonhalbe Feb 10 '12 at 13:58
  • 1
    @sigfpe Side comment: You would have to look for `[String] -> String -> String` in case that the other way returns no answer, right? – Lay González Mar 08 '14 at 21:41
  • 1
    @LayGonzález The search is up to permutations. For instance searching for [`[a] -> (a -> b) -> [b]`](https://www.haskell.org/hoogle/?hoogle=%5Ba%5D+-%3E+%28a+-%3E+b%29+-%3E+%5Bb%5D) returns `map` as its first result. – gallais Jun 18 '16 at 23:15

5 Answers5

265

Yes, there is:

Prelude> import Data.List
Prelude Data.List> intercalate " " ["is","there","such","a","function","?"]
"is there such a function ?"

intersperse is a bit more general:

Prelude> import Data.List
Prelude Data.List> concat (intersperse " " ["is","there","such","a","function","?"])
"is there such a function ?"

Also, for the specific case where you want to join with a space character, there is unwords:

Prelude> unwords ["is","there","such","a","function","?"]
"is there such a function ?"

unlines works similarly, only that the strings are imploded using the newline character and that a newline character is also added to the end. (This makes it useful for serializing text files, which must per POSIX standard end with a trailing newline)

adius
  • 13,685
  • 7
  • 45
  • 46
Niklas B.
  • 92,950
  • 18
  • 194
  • 224
  • Can any of it deal with possible empty strings? – CMCDragonkai Jun 23 '15 at 11:03
  • 3
    @CMCDragonkai Not sure what exactly you're referring to, but yes, these functions all allow arbitrary strings as both the separator and the elements. For example, `intercalate "," ["some", "", "string"] = "some,,string"` and `intercalate "" ["foo", "bar"] = "foobar"` – Niklas B. Jun 23 '15 at 11:51
  • 3
    `unlines` adds a newline to each line, that is `unlines ["A", "B"] = "A\nB\n"`, so it is not the same as intercalate. – Kathy Van Stone Apr 26 '17 at 22:33
  • @KathyVanStone Interesting, I guess I never tried and only assumed it works analogously to `unwords`. – Niklas B. Apr 27 '17 at 18:23
  • 1
    It's nice that there are some normal string and list manipulation functions in the standard library, and it's nice that you're posting an example here, because it's pretty hard to find any documentation for this kind of everyday programming in Haskell. – Andrew Koster Jul 02 '20 at 16:09
  • Is there a way to add a predicate in intersperse? If the condition is met, then insert the " " between each item in the list. – plasma Nov 11 '21 at 18:48
7

It's not hard to write one-liner using foldr

join sep xs = foldr (\a b-> a ++ if b=="" then b else sep ++ b) "" xs
join " " ["is","there","such","a","function","?"]
Ilya Kharlamov
  • 3,698
  • 1
  • 31
  • 33
  • 6
    It would be beneficial to add a description to this; someone flagged it as low quality. – Arya McCarthy May 22 '17 at 01:15
  • 1
    This concept of folding is sorta like reduce (if you know what reduce is). Foldr (fold right) applies a function to each element in a list and adds the result to an accumulator. It then returns the accumulated value. Here we are declaring a function `join` which accepts a separator and a string list. It then applies fold on the list using an anonymous lambda function. In `(\a b-> a ++ if b=="" then b else sep ++ b)` a is the accumulator, b is the next item on the list. The fold starts with "" – trevdev Jan 20 '23 at 15:23
4

Some other ideas of implementations of intersperse and intercalate, if someone is interested:

myIntersperse :: a -> [a] -> [a]
myIntersperse _ [] = []
myIntersperse e xs = init $ xs >>= (:[e])

myIntercalate :: [a] -> [[a]] -> [a]
myIntercalate e xs = concat $ myIntersperse e xs

xs >>= f is equivalent to concat (map f xs).

Alexis
  • 141
  • 3
  • 9
3
joinBy sep cont = drop (length sep) $ concat $ map (\w -> sep ++ w) cont
Alaya
  • 3,287
  • 4
  • 27
  • 39
3

If you wanted to write your own versions of intercalate and intersperse:

intercalate :: [a] -> [[a]] -> [a]
intercalate s [] = []
intercalate s [x] = x
intercalate s (x:xs) = x ++ s ++ (intercalate s xs)

intersperse :: a -> [a] -> [a]
intersperse s [] = []
intersperse s [x] = [x]
intersperse s (x:xs) = x : s : (intersperse s xs)
Zoey Hewll
  • 4,788
  • 2
  • 20
  • 33
  • 1
    Why limit yourself to strings? Also, your parens around the function application are redundant. – melpomene Feb 04 '17 at 10:22
  • True, `intersperse` needn't be `Strings`, but `intercalate` would need to at least be `Show`, and if you _did_ use `Show`, you'd need some way to deal with them using `String`s anyway. I'm still getting used to how Haskell deals with mixed infix and prefix functions/operators, and I prefer bracketing when mixing in case I end up wanting to use `$` – Zoey Hewll Feb 04 '17 at 10:27
  • `intercalate :: [a] -> [[a]] -> [a]` - why `Show`? As for syntax, Haskell doesn't have any prefix operators (except for `-`, which is an abomination), and function application binds tighter than any infix operator: `x:s:intersperse s xs` is fine (but it reads much better if you put the spaces in: `x : s : intersperse s xs` (I don't really understand why people like to leave out the spaces around `:`)). – melpomene Feb 04 '17 at 10:32
  • Right. I keep forgetting that working with strings is just working with lists. `Show` was because I was assuming you'd want the result to be a `String`. By "infix and prefix functions/operators" I meant "prefix functions and infix operators", but that was unclear. Unary `-` is death. As for the `:`s and other infix operators, whether I use spaces depends highly on context, but I'm always locally consistent. eg, `(:)` in a pattern match never has spaces, but elsewhere it depends on whether it's bracketed and on my mood. – Zoey Hewll Feb 04 '17 at 10:40