packages feed

group-with 0.2.0.2 → 0.2.0.3

raw patch · 5 files changed

+79/−69 lines, 5 filesdep ~basePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base

API changes (from Hackage documentation)

Files

Control/GroupWith.hs view
@@ -7,23 +7,23 @@ -- Maintainer  :  ukoehler@techoverflow.net -- Stability   :  provisional -- Portability :  portable--- +-- -- A collection of grouping utility functions. -- For a given function that assigns a key to objects, -- provides functions that group said objects into a multimap -- by said key.--- +-- -- This can be used similarly to the SQL GROUP BY statement.--- +-- -- Provides a more flexible approach to GHC.Exts.groupWith--- +-- -- > groupWith (take 1) ["a","ab","bc"] == Map.fromList [("a",["a","ab"]), ("b",["bc"])] -- -- In order to use monadic / applicative functions as key generators, -- use the A- or M-postfixed variants like 'groupWithA' or 'groupWithMultipleM' ----- ---  +--+-- ----------------------------------------------------------------------------- module Control.GroupWith(         MultiMap,@@ -50,8 +50,8 @@ --   for each generated key the values (from the given list) --   that yielded said key by applying the function on said value. groupWith :: (Ord b) =>-             (a -> b) -- ^ The function used to map a list value to its key-          -> [a] -- ^ The list to be grouped+             (a -> b)     -- ^ The function used to map a list value to its key+          -> [a]          -- ^ The list to be grouped           -> MultiMap b a -- ^ The resulting key --> value multimap groupWith f xs = Map.fromListWith (++) [(f x, [x]) | x <- xs] @@ -60,30 +60,33 @@ --   The corresponding value from the original list will be placed --   in the identifier-corresponding map entry for each generated --   identifier.---   Note that values are added to the +--   Note that values are added to the groupWithMultiple :: (Ord b) =>-                     (a -> [b]) -- ^ The function used to map a list value to its keys-                  -> [a] -- ^ The list to be grouped+                     (a -> [b])   -- ^ The function used to map a list value to+                                  --   its keys+                  -> [a]          -- ^ The list to be grouped                   -> MultiMap b a -- ^ The resulting map-groupWithMultiple f xs = +groupWithMultiple f xs =   let identifiers x = [(val, [x]) | val <- vals] where vals = f x   in Map.fromListWith (++) $ concat [identifiers x | x <- xs]  -- | Like groupWith, but uses a custom combinator function groupWithUsing :: (Ord b) =>-             (a -> c) -- ^ Transformer function used to map a value to the resulting type+             (a -> c)      -- ^ Transformer function used to map a value to the+                           --   resulting type           -> (c -> c -> c) -- ^ The combinator used to combine an existing value                            --   for a given key with a new value-          -> (a -> b) -- ^ The function used to map a list value to its key-          -> [a] -- ^ The list to be grouped-          -> Map b c -- ^ The resulting key --> transformed value map+          -> (a -> b)      -- ^ The function used to map a list value to its key+          -> [a]           -- ^ The list to be grouped+          -> Map b c       -- ^ The resulting key --> transformed value map groupWithUsing t c f xs = Map.fromListWith c $ map (\v -> (f v, t v)) xs  -- | Fuse the functor from a tuple fuseT2 :: Applicative f => (f a, f b) -> f (a,b) fuseT2 = uncurry $ liftA2 (,) --- | Like 'fuseT2', but only requires the first element to be boxed in the functor+-- | Like 'fuseT2', but only requires the first element to be boxed in the+--   functor fuseFirst :: Applicative f => (f a, b) -> f (a,b) fuseFirst = fuseT2 . second pure @@ -96,41 +99,43 @@ -- | Group values in a list by a key, generated by a given applicative function. --   Applicative version of 'groupWith'. See 'groupWith' for documentation. groupWithA :: (Ord b, Applicative f) =>-             (a -> f b) -- ^ The function used to map a list value to its key-          -> [a] -- ^ The list to be grouped-          -> f (MultiMap b a) -- ^ The resulting key --> value multimap+          (a -> f b)       -- ^ The function used to map a list value to its key+       -> [a]              -- ^ The list to be grouped+       -> f (MultiMap b a) -- ^ The resulting key --> value multimap groupWithA f xs =   Map.fromListWith (++) <$> fuseFirstList [(f x, [x]) | x <- xs]  -- | Alias for 'groupWithA', with additional monad constraint groupWithM :: (Ord b, Monad m, Applicative m) =>-             (a -> m b) -- ^ The function used to map a list value to its key-          -> [a] -- ^ The list to be grouped-          -> m (MultiMap b a) -- ^ The resulting key --> value multimap+          (a -> m b)       -- ^ The function used to map a list value to its key+       -> [a]              -- ^ The list to be grouped+       -> m (MultiMap b a) -- ^ The resulting key --> value multimap groupWithM = groupWithA  -- | Like 'groupWithM', but the identifier-generating function --   may generate multiple keys for each value (or none at all). --   See 'groupWithMultiple' for further behavioural details.---   +-- --   Note that it's impossible to define this for applicatives: --   See http://stackoverflow.com/a/6032260/2597135 groupWithMultipleM :: (Ord b, Monad m, Applicative m) =>-                     (a -> m [b]) -- ^ The function used to map a list value to its keys-                  -> [a] -- ^ The list to be grouped+                     (a -> m [b])     -- ^ The function used to map a list value+                                      --   to its keys+                  -> [a]              -- ^ The list to be grouped                   -> m (MultiMap b a) -- ^ The resulting map-groupWithMultipleM f xs = +groupWithMultipleM f xs =   let identifiers x = (\vals -> [(val, [x]) | val <- vals]) <$> f x       idMap = concat <$> (mapM identifiers xs)   in Map.fromListWith (++) <$> idMap  -- | Like 'groupWithM', but uses a custom combinator function groupWithUsingM :: (Ord b, Monad m, Applicative m) =>-             (a -> m c) -- ^ Transformer function used to map a value to the resulting type+             (a -> m c)    -- ^ Transformer function used to map a value to the+                           --   resulting type           -> (c -> c -> c) -- ^ The combinator used to combine an existing value                            --   for a given key with a new value-          -> (a -> m b) -- ^ The function used to map a list value to its key-          -> [a] -- ^ The list to be grouped-          -> m (Map b c) -- ^ The resulting key --> transformed value map+          -> (a -> m b)    -- ^ The function used to map a list value to its key+          -> [a]           -- ^ The list to be grouped+          -> m (Map b c)   -- ^ The resulting key --> transformed value map groupWithUsingM t c f xs =   Map.fromListWith c <$> mapM (\v -> fuseT2 (f v, t v)) xs
GroupWithTest.hs view
@@ -25,19 +25,22 @@ multimapTupleEq :: (Ord a, Ord b, Eq a, Eq b) => (Map a [b], Map a [b]) -> Bool multimapTupleEq (x,y) = multimapEq x y +never_called_fn :: a+never_called_fn =  error "This function should never be called"+ main :: IO () main = hspec $ do   describe "groupWith" $ do     it "should group a simple value correctly" $-        let data_ = ["a","abc","ab","bc"]-            fn = take 1-            ref = Map.fromList [("a",["a","abc","ab"]),("b",["bc"])]+        let data_  = ["a","abc","ab","bc"]+            fn     = take 1+            ref    = Map.fromList [("a",["a","abc","ab"]),("b",["bc"])]             result = groupWith fn data_         in (result, ref) `shouldSatisfy` multimapTupleEq     it "should return an empty map when given an empty list" $         -- We need to specialize here, because it's ⊥-        let fn = error "This function should never be called" :: Int -> Int-            data_ = [] :: [Int]+        let fn     = never_called_fn :: Int -> Int+            data_  = [] :: [Int]             result = groupWith fn data_         in result `shouldSatisfy` ((==) 0 . Map.size)     -- Fuzzing, couldn't get this to compile properly yet@@ -46,23 +49,23 @@   describe "groupWithUsing" $ do     it "should return an empty map when given an empty list" $         -- We need to specialize here, because it's ⊥-        let fn = error "This function should never be called" :: Int -> Int-            data_ = [] :: [Int]+        let fn     = never_called_fn :: Int -> Int+            data_  = [] :: [Int]             result = groupWithUsing id (+) fn data_         in result `shouldSatisfy` ((==) 0 . Map.size)     it "should be usable for counting" $         -- Instead of building up lists, we count the number of elements-        let t n = 1 -- For each x, count 1-            c = (+) -- Sum up the counts-            fn = take 1+        let t n   = 1   -- For each x, count 1+            c     = (+) -- Sum up the counts+            fn    = take 1             data_ = ["a","abc","ab","bc"]-            ref = Map.fromList [("a",3),("b",1)] :: Map String Int+            ref   = Map.fromList [("a",3),("b",1)] :: Map String Int             result = groupWithUsing t c fn data_         in result `shouldBe` ref   describe "groupWithMultiple" $ do     it "should group correctly given a simple list" $         let data_ = ["a","abc","ab","bc"]-            fn x = [take 1 x, take 2 x]+            fn x  = [take 1 x, take 2 x]             -- Note the multiple "a"s in the first line:             -- one from `take 1`, one from `take 2`             ref = Map.fromList [("a",["a","a","abc","ab"]),@@ -73,8 +76,8 @@         in (result, ref) `shouldSatisfy` multimapTupleEq     it "should return an empty map when given an empty list" $         -- We need to specialize here, because it's ⊥-        let fn = error "This function should never be called" :: Int -> [Int]-            data_ = [] :: [Int]+        let fn     = never_called_fn :: Int -> [Int]+            data_  = [] :: [Int]             result = groupWithMultiple fn data_         in result `shouldSatisfy` ((==) 0 . Map.size)   {-@@ -82,15 +85,15 @@   -}   describe "groupWithM" $ do     it "should group a simple value correctly" $-        let data_ = ["a","abc","ab","bc"]-            fn = Just . take 1-            ref = Map.fromList [("a",["a","abc","ab"]),("b",["bc"])]+        let data_  = ["a","abc","ab","bc"]+            fn     = Just . take 1+            ref    = Map.fromList [("a",["a","abc","ab"]),("b",["bc"])]             result = groupWithM fn data_         in (fromJust result, ref) `shouldSatisfy` multimapTupleEq     it "should return an empty map when given an empty list" $         -- We need to specialize here, because it's ⊥-        let fn = error "This function should never be called" :: Int -> Maybe Int-            data_ = [] :: [Int]+        let fn     = never_called_fn :: Int -> Maybe Int+            data_  = [] :: [Int]             result = groupWithM fn data_         in (fromJust result) `shouldSatisfy` ((==) 0 . Map.size)     -- Fuzzing, couldn't get this to compile properly yet@@ -110,23 +113,23 @@         in (fromJust result, ref) `shouldSatisfy` multimapTupleEq     it "should return an empty map when given an empty list" $         -- We need to specialize here, because it's ⊥-        let fn = error "This function should never be called" :: Int -> Maybe [Int]-            data_ = [] :: [Int]+        let fn     = never_called_fn :: Int -> Maybe [Int]+            data_  = [] :: [Int]             result = groupWithMultipleM fn data_         in (fromJust result) `shouldSatisfy` ((==) 0 . Map.size)   describe "groupWithUsingM" $ do     it "should return an empty map when given an empty list" $         -- We need to specialize here, because it's ⊥-        let fn = error "This function should never be called" :: Int -> Maybe Int-            data_ = [] :: [Int]+        let fn     = never_called_fn :: Int -> Maybe Int+            data_  = [] :: [Int]             result = groupWithUsingM return (+) fn data_         in (fromJust result) `shouldSatisfy` ((==) 0 . Map.size)     it "should be usable for counting" $         -- Instead of building up lists, we count the number of elements-        let t n = return 1 -- For each x, count 1-            c = (+) -- Sum up the counts-            fn = return . take 1-            data_ = ["a","abc","ab","bc"]-            ref = Map.fromList [("a",3),("b",1)] :: Map String Int+        let t n    = return 1 -- For each x, count 1+            c      = (+)      -- Sum up the counts+            fn     = return . take 1+            data_  = ["a","abc","ab","bc"]+            ref    = Map.fromList [("a",3),("b",1)] :: Map String Int             result = groupWithUsingM t c fn data_         in fromJust result `shouldBe` ref
README.md view
@@ -1,4 +1,5 @@ group-with-========+========== -A Haskell library to classify objects by a function value, just like SQL GROUP BY+A Haskell library to classify objects by a function value, just like SQL GROUP+BY.
changelog view
@@ -1,12 +1,12 @@ ** 0.2.x  0.2.0.0 --> 0.2.0.2-===============+===================  * Fix dependency issue: Can't build with containers < 0.5.0.0  0.1.0.0 --> 0.2.0.0-===============+===================  * Support monadic and applicative (where possible) grouping 
group-with.cabal view
@@ -1,8 +1,9 @@ name:                group-with-version:             0.2.0.2-synopsis:            Classify objects by key-generating function, like SQL GROUP BY-description:         A library to classify objects into multimaps by using a function generating-                     keys for any object in the list.+version:             0.2.0.3+synopsis:            Classify objects by key-generating function,+                     like SQL GROUP BY+description:         A library to classify objects into multimaps by using a+                     function generating keys for any object in the list.                       This allows an approach similar to SQL GROUP BY. @@ -12,7 +13,7 @@ license-file:        LICENSE author:              Uli Köhler maintainer:          ukoehler@techoverflow.net--- copyright:           +-- copyright: category:            Data build-type:          Simple extra-source-files:  README.md changelog@@ -26,7 +27,7 @@   exposed-modules: Control.GroupWith   -- other-modules:   -- other-extensions:-  build-depends:       base >= 4.2 && < 4.8, containers >= 0.5+  build-depends:       base >= 4.2 && < 4.9, containers >= 0.5   -- hs-source-dirs:   default-language:    Haskell2010