diff --git a/CHANGES.txt b/CHANGES.txt index ac8b058..d42e3f4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,7 @@ Changelog for Extra + Add maximumOn and minimumOn + #31, add nubSort, nubSortBy and nubSortOn 1.6.2, released 2017-12-07 Mark the partial functions with Partial Add Partial constraint diff --git a/src/Data/List/Extra.hs b/src/Data/List/Extra.hs index 9e0d445..8bde634 100644 --- a/src/Data/List/Extra.hs +++ b/src/Data/List/Extra.hs @@ -9,7 +9,7 @@ module Data.List.Extra( module Data.List, -- * String operations lower, upper, trim, trimStart, trimEnd, word1, line1, - -- * Splitting + -- * Splitting dropEnd, takeEnd, splitAtEnd, breakEnd, spanEnd, dropWhileEnd, dropWhileEnd', takeWhileEnd, stripSuffix, stripInfix, stripInfixEnd, @@ -21,6 +21,8 @@ module Data.List.Extra( groupSort, groupSortOn, groupSortBy, nubOrd, nubOrdBy, nubOrdOn, nubOn, groupOn, sortOn, + nubSort, nubSortBy, nubSortOn, + maximumOn, minimumOn, disjoint, allSame, anySame, repeatedly, for, firstJust, concatUnzip, concatUnzip3, @@ -296,6 +298,13 @@ groupOn f = groupBy ((==) `on2` f) nubOn :: Eq b => (a -> b) -> [a] -> [a] nubOn f = map snd . nubBy ((==) `on` fst) . map (\x -> let y = f x in y `seq` (y, x)) +-- | A version of 'maximum' where the comparison is done on some extracted value. +maximumOn :: Ord b => (a -> b) -> [a] -> a +maximumOn f = maximumBy (compare `on` f) + +-- | A version of 'minimum' where the comparison is done on some extracted value. +minimumOn :: Ord b => (a -> b) -> [a] -> a +minimumOn f = minimumBy (compare `on` f) -- | A combination of 'group' and 'sort'. -- @@ -548,6 +557,29 @@ chunksOf i xs | i <= 0 = error $ "chunksOf, number must be positive, got " ++ sh chunksOf i xs = repeatedly (splitAt i) xs +-- | /O(n log n)/. The 'nubSort' function sorts and removes duplicate elements from a list. +-- In particular, it keeps only the first occurrence of each element. +-- +-- > nubSort "this is a test" == " aehist" +-- > \xs -> nubSort xs == nub (sort xs) +nubSort :: Ord a => [a] -> [a] +nubSort = nubSortBy compare + +-- | A version of 'nubSort' which operates on a portion of the value. +-- +-- > nubSortOn length ["a","test","of","this"] == ["a","of","test"] +nubSortOn :: Ord b => (a -> b) -> [a] -> [a] +nubSortOn f = nubSortBy (compare `on` f) + +-- | A version of 'nubSort' with a custom predicate. +-- +-- > nubSortBy (compare `on` length) ["a","test","of","this"] == ["a","of","test"] +nubSortBy :: (a -> a -> Ordering) -> [a] -> [a] +nubSortBy cmp = f . sortBy cmp + where f (x1:x2:xs) | cmp x1 x2 == EQ = f (x1:xs) + f (x:xs) = x : f xs + f [] = [] + -- | /O(n log n)/. The 'nubOrd' function removes duplicate elements from a list. -- In particular, it keeps only the first occurrence of each element. -- Unlike the standard 'nub' operator, this version requires an 'Ord' instance diff --git a/src/Extra.hs b/src/Extra.hs index 1d8c2a1..0a8aabe 100644 --- a/src/Extra.hs +++ b/src/Extra.hs @@ -23,7 +23,7 @@ module Extra {-# DEPRECATED "This module is provided as documentation of all new modifyIORef', writeIORef', atomicModifyIORef', atomicWriteIORef, atomicWriteIORef', -- * Data.List.Extra -- | Extra functions available in @"Data.List.Extra"@. - lower, upper, trim, trimStart, trimEnd, word1, line1, dropEnd, takeEnd, splitAtEnd, breakEnd, spanEnd, dropWhileEnd, dropWhileEnd', takeWhileEnd, stripSuffix, stripInfix, stripInfixEnd, wordsBy, linesBy, breakOn, breakOnEnd, splitOn, split, chunksOf, list, uncons, unsnoc, cons, snoc, drop1, mconcatMap, groupSort, groupSortOn, groupSortBy, nubOrd, nubOrdBy, nubOrdOn, nubOn, groupOn, sortOn, disjoint, allSame, anySame, repeatedly, for, firstJust, concatUnzip, concatUnzip3, zipFrom, zipWithFrom, replace, merge, mergeBy, + lower, upper, trim, trimStart, trimEnd, word1, line1, dropEnd, takeEnd, splitAtEnd, breakEnd, spanEnd, dropWhileEnd, dropWhileEnd', takeWhileEnd, stripSuffix, stripInfix, stripInfixEnd, wordsBy, linesBy, breakOn, breakOnEnd, splitOn, split, chunksOf, list, uncons, unsnoc, cons, snoc, drop1, mconcatMap, groupSort, groupSortOn, groupSortBy, nubOrd, nubOrdBy, nubOrdOn, nubOn, groupOn, sortOn, nubSort, nubSortBy, nubSortOn, maximumOn, minimumOn, disjoint, allSame, anySame, repeatedly, for, firstJust, concatUnzip, concatUnzip3, zipFrom, zipWithFrom, replace, merge, mergeBy, -- * Data.Tuple.Extra -- | Extra functions available in @"Data.Tuple.Extra"@. first, second, (***), (&&&), dupe, both, fst3, snd3, thd3, diff --git a/test/TestGen.hs b/test/TestGen.hs index 9e6c79a..b857c46 100755 --- a/test/TestGen.hs +++ b/test/TestGen.hs @@ -194,6 +194,10 @@ tests = do testGen "chunksOf 3 \"mytest\" == [\"myt\",\"est\"]" $ chunksOf 3 "mytest" == ["myt","est"] testGen "chunksOf 8 \"\" == []" $ chunksOf 8 "" == [] testGen "chunksOf 0 \"test\" == undefined" $ erroneous $ chunksOf 0 "test" + testGen "nubSort \"this is a test\" == \" aehist\"" $ nubSort "this is a test" == " aehist" + testGen "\\xs -> nubSort xs == nub (sort xs)" $ \xs -> nubSort xs == nub (sort xs) + testGen "nubSortOn length [\"a\",\"test\",\"of\",\"this\"] == [\"a\",\"of\",\"test\"]" $ nubSortOn length ["a","test","of","this"] == ["a","of","test"] + testGen "nubSortBy (compare `on` length) [\"a\",\"test\",\"of\",\"this\"] == [\"a\",\"of\",\"test\"]" $ nubSortBy (compare `on` length) ["a","test","of","this"] == ["a","of","test"] testGen "nubOrd \"this is a test\" == \"this ae\"" $ nubOrd "this is a test" == "this ae" testGen "nubOrd (take 4 (\"this\" ++ undefined)) == \"this\"" $ nubOrd (take 4 ("this" ++ undefined)) == "this" testGen "\\xs -> nubOrd xs == nub xs" $ \xs -> nubOrd xs == nub xs