Skip to content

Commit

Permalink
Add nubSort and maximumOn/minimumOn
Browse files Browse the repository at this point in the history
  • Loading branch information
ndmitchell committed Jan 26, 2018
1 parent 55d6973 commit 83df7b8
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 2 deletions.
2 changes: 2 additions & 0 deletions 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
Expand Down
34 changes: 33 additions & 1 deletion src/Data/List/Extra.hs
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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'.
--
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/Extra.hs
Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions test/TestGen.hs
Expand Up @@ -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
Expand Down

0 comments on commit 83df7b8

Please sign in to comment.