-- | List operations, part of the "Useful" module.
module Useful.List where
import Useful.General
import Data.List
-- | Takes a list item and splits a list around it, removing the item.
--
-- > $ explodeI "Hello there people" ' '
-- > ["Hello","there","people"]
explodeI :: Eq a => [a] -> a -> [[a]]
explodeI xs m = rmEmpty (explode' xs m)
-- explode' :: Eq a => [a] -> a -> [[a]]
explode' [] m = []
explode' xs m = [takeWhile (!=m) xs] ++ explode' (tail' (dropWhile (!=m) xs)) m
where
tail' [] = []
tail' (x:xs) = xs
-- | Alias of explodeI
splitI :: Eq a => [a] -> a -> [[a]]
splitI = explodeI
-- | Take a list item and concatinates each element of it around another given item.
--
-- > $implodeI "askjdnaskd" '!'
-- > "a!s!k!j!d!n!a!s!k!d"
implodeI :: Eq a => [a] -> a -> [a]
implodeI (x:xs) y
|xs == [] = [x]
|otherwise = (x : [y]) ++ (implodeI xs y)
-- | alias of implodeI
joinI :: Eq a => [a] -> a -> [a]
joinI = implodeI
-- | Takes a two lists and explodes the first into a new list, around the second. Removing the second list where it occurs.
--
-- > $explode "hello there people" "ll"
-- > ["he","o there people"]
-- > $explode "hello there people" " "
-- > ["hello","there","people"]
explode :: Eq a => [a] -> [a] -> [[a]]
explode x y = explode'' x y 0 0
-- explode'' :: Eq a => [a] -> [a] -> Int -> Int -> [[a]]
explode'' x y buff count
|x == y = []
|x == [] = []
|y == [] = [x]
|count+buff == len x = [x]
|(len y) == buff = (fst splut) : (explode'' (drop buff (snd splut)) y 0 0) -- If the buffer is full (there is a full match) then split the string and explode the rest.
|(x !! (count+buff)) == (y !! buff) = explode'' x y (buff+1) count -- If the character matches increase the buffer
|otherwise = explode'' x y 0 (count+1) -- otherwise just increment the counter.
where splut = (splitAt (count) x)
-- | alias of explode
split :: Eq a => [a] -> [a] -> [[a]]
split = explode
-- | Takes a list of lists and an extra list and concatinates the list of lists with the second list inbetween. When used with the empty list mimics concat
--
-- > $ implode ["helloasdad","asd hello","hello"] "!!"
-- > "helloasdad!!asd hello!!hello"
implode :: Eq a => [[a]] -> [a] -> [a]
implode x [] = concat x
implode (x:xs) y
|xs == [] = x
|otherwise = (x ++ y) ++ (implode xs y)
-- | Alias of implode
join :: Eq a => [[a]] -> [a] -> [a]
join = implode
-- | takes a number of items from a list before it reaches the index n
--
-- > $ takeBefore 5 "Hello there people"
-- > "Hello there p"
-- takeBefore :: Eq a => Int -> [a] -> [a]
-- takeBefore n x = take (len x - n) x 0
-- | drops a number of items from a list before it reaches the index n
--
-- > $ dropBefore 5 "Hello there people"
-- > "eople"
-- dropBefore :: Eq a => Int -> [a] -> [a]
-- dropBefore n x = drop (len x - n) x 0
-- | In a list of lists this removes any occurances of the empty list. Can also be used to remove occurances of the empty string.
--
-- > $rmEmpty ["hello","","there","","people",""]
-- > ["hello","there","people"]
rmEmpty :: Eq a => [[a]] -> [[a]]
rmEmpty [] = []
rmEmpty (x:xs)
|x == [] = rmEmpty xs
|otherwise = x:(rmEmpty xs)
-- | maps a function in depth N to the given list. map3, map4, map5 are also defined.
--
-- > $ map2 (*2) [[1,2,3,4],[1,1,1,2]]
-- > [[2,4,6,8],[2,2,2,4]]
map2 :: (a -> b) -> [[a]] -> [[b]]
map2 f x = map (map f) x
map3 :: (a -> b) -> [[[a]]] -> [[[b]]]
map3 f x = map (map (map f)) x
map4 :: (a -> b) -> [[[[a]]]] -> [[[[b]]]]
map4 f x = map (map (map (map f))) x
map5 :: (a -> b) -> [[[[[a]]]]] -> [[[[[b]]]]]
map5 f x = map (map (map (map (map f)))) x
-- | Replaces any occurrences of the second list, with the third list, in the first list.
--
-- > $ replace "why hello hello there" "hello" "bonjour"
-- > "why bonjour bonjour there"
replace :: Eq a => [a] -> [a] -> [a] -> [a]
replace s [] x = s
replace [] _ _ = []
replace s find repl
|take (length find) s == find = repl ++ (replace (drop (length find) s) find repl)
|otherwise = [head s] ++ (replace (tail s) find repl)
-- | Takes a list of items and returns a list with each element in it's own single list.
--
-- > $ each "hello"
-- > ["h","e","l","l","o"]
each :: [a] -> [[a]]
each x = f x
where
f :: [a] -> [[a]]
f [] = []
f (x:xs) = [x] : f xs