module Euler.Util.List
(
neTails
, sliding
, transpose
, untilNothing
, maximumOn
, countDistinct
, mode
, dedupe
, adjustEach
, adjustIndex
) where
import Euler.Prelude
import Data.List ((!!))
import qualified Data.List as List
import qualified Data.List.NonEmpty as NE
import qualified Data.MultiSet as MultiSet
import qualified Data.Set as Set
neTails :: NonEmpty a -> NonEmpty (NonEmpty a)
neTails = NE.fromList . catMaybes . NE.toList . fmap NE.nonEmpty . NE.tails
sliding :: Int -> [a] -> [[a]]
sliding _ [] = []
sliding n xs@(_:ys) =
if List.length xs >= n
then List.take n xs : sliding n ys
else []
transpose :: [[a]] -> [[a]]
transpose [] = []
transpose ([]:_) = []
transpose xs = (List.head <$> xs) : transpose (List.tail <$> xs)
untilNothing :: [Maybe a] -> [a]
untilNothing = catMaybes . List.takeWhile isJust
maximumOn :: Ord b => (a -> b) -> [a] -> a
maximumOn f = fst . maximumBy (compare `on` snd) . fmap (\x -> (x, f x))
countDistinct :: (Ord a, Integral b) => [a] -> b
countDistinct = fromIntegral . List.length . Set.fromList
mode :: Ord a => [a] -> a
mode = fst . maximumOn snd . MultiSet.toOccurList . MultiSet.fromList
dedupe :: Eq a => [a] -> [a]
dedupe = fmap List.head . group
adjustIndex :: Int -> (a -> a) -> [a] -> [a]
adjustIndex i f xs = List.take i xs <> [f $ xs !! i] <> List.drop (i+1) xs
adjustEach :: (a -> a) -> [a] -> [[a]]
adjustEach f xs = [ adjustIndex i f xs | i <- [0 .. List.length xs - 1] ]