Reputation: 23
I'm trying to write a function with the type declaration [(Int, Bool)] -> [[Int]]
. I want the function to only add Int
s to the same nested sublist if the Boolean is True
. However if the Boolean is False
, I want the Int
associated with the next True
bool to be added to a new sublist. For example: An input of
[(1,True),(2,True),(3,False),(4,True),(5,False),(6,False),(7,True)]
should return
[[1,2],[4],[7]].
My code so far:
test:: [(Int, Bool)] -> [[Int]]
test xs = case xs of
[]->[]
x:xs
| snd x == True -> [(fst x)] : test xs
| snd x == False -> test xs
I'm currently having issues on adding concurrent Ints to the same list if their bools are both True
.
Upvotes: 2
Views: 533
Reputation: 48647
Here's how I'd write this:
import Data.List.NonEmpty (NonEmpty(..), (<|))
import qualified Data.List.NonEmpty as NE
test :: [(Int, Bool)] -> [[Int]]
test = NE.filter (not . null) . foldr go ([]:|[])
where
go :: (Int, Bool) -> NonEmpty [Int] -> NonEmpty [Int]
go (n, True) ~(h:|t) = (n:h):|t
go (n, False) l = []<|l
Or with Will Ness's suggestion:
import Data.List.NonEmpty (NonEmpty(..))
test :: [(Int, Bool)] -> [[Int]]
test = removeHeadIfEmpty . foldr prependOrStartNewList ([]:|[])
where
prependOrStartNewList :: (Int, Bool) -> NonEmpty [Int] -> NonEmpty [Int]
prependOrStartNewList (n, True) ~(h:|t) = (n:h):|t
prependOrStartNewList (n, False) l = []:|removeHeadIfEmpty l
removeHeadIfEmpty :: NonEmpty [Int] -> [[Int]]
removeHeadIfEmpty (h:|t) = if null h then t else h:t
Upvotes: 2
Reputation: 71109
Repeatedly, drop the False
s, grab the True
s. With view patterns:
{-# LANGUAGE ViewPatterns #-}
test :: [(a, Bool)] -> [[a]]
test (span snd . dropWhile (not . snd) -> (a,b))
| null a = []
| otherwise = map fst a : test b
Works with infinite lists as well, inasmuch as possible.
Upvotes: 2
Reputation: 1077
You can break this problem into two sub-problems.
collectF :: (Eq a) => (a -> Bool) -> [a] -> ([a], [a]) collectF f [] = ([], []) collectF f (x : xs) | f x = let (ys, zs) = collectF f xs in (x : ys, zs) | otherwise = ([], x : xs)
groupBy :: (Eq a) => (a -> a -> Bool) -> [a] -> [[a]] groupBy _ [] = [] groupBy f (x : xs) = let (ys, zs) = collectF (f x) xs in (x : ys) : groupBy f zs *Main> groupBy (\x y -> snd x == snd y) [(1,True),(2,True),(3,False),(4,True),(5,False),(6,False),(7,True)] [[(1,True),(2,True)],[(3,False)],[(4,True)],[(5,False),(6,False)],[(7,True)]]
I am leaving it to you to remove the True and False values from List. Also, have a look at List library of Haskell [1]. Hope, I am clear enough, but let me know if you have any other question.
[1] http://hackage.haskell.org/package/base-4.12.0.0/docs/src/Data.OldList.html#groupBy
Upvotes: 2