diff --git a/src/Array/Extra.elm b/src/Array/Extra.elm index b93a061..9062422 100644 --- a/src/Array/Extra.elm +++ b/src/Array/Extra.elm @@ -1,8 +1,8 @@ module Array.Extra exposing - ( all, any, member + ( all, any, firstJustMap, member , reverse, intersperse , update, pop, removeAt, insertAt - , removeWhen, filterMap + , removeWhen, filterMap, allJustMap , sliceFrom, sliceUntil, splitAt, unzip , interweave, apply, map2, map3, map4, map5, zip, zip3 , resizelRepeat, resizerRepeat, resizelIndexed, resizerIndexed @@ -14,7 +14,7 @@ module Array.Extra exposing # observe -@docs all, any, member +@docs all, any, firstJustMap, member # alter @@ -25,7 +25,7 @@ module Array.Extra exposing ## filter -@docs removeWhen, filterMap +@docs removeWhen, filterMap, allJustMap ## part @@ -179,20 +179,110 @@ filterMap tryMap = \array -> array |> Array.foldr - (\el soFar -> soFar |> consTry (el |> tryMap)) + (\el soFar -> soFar |> consJust (el |> tryMap)) [] |> Array.fromList -consTry : Maybe a -> List a -> List a -consTry maybeNewHead = - \list -> - case maybeNewHead of - Just newHead -> - newHead :: list +consJust : Maybe a -> List a -> List a +consJust maybeNewHead = + case maybeNewHead of + Just newHead -> + \list -> list |> (::) newHead - Nothing -> - list + Nothing -> + identity + + +{-| After applying the given function to every element, +try unpacking _all_ `Just` values. +If just a single element is `Nothing`, `Nothing` is returned. + +This is like a nicer [`all`](#all) where if you test for certain elements you already get +the `Array` with all the successful results or `Nothing` if one wasn't successful. + + import Array exposing (empty, fromList) + + empty + |> allJustMap String.toInt + --> Just empty + + fromList [ "1", "2", "3" ] + |> allJustMap String.toInt + --> Just (fromList [ 1, 2, 3 ]) + + fromList [ "1", "NaN", "3" ] + |> allJustMap String.toInt + --> Nothing + +This is often called "traverse". +A version with óut the map is often called "sequence" +and `Maybe.Extra` calls it [`combine`](https://package.elm-lang.org/packages/elm-community/maybe-extra/latest/Maybe-Extra#combineArray) + +-} +allJustMap : + (element -> Maybe narrowElement) + -> Array element + -> Maybe (Array narrowElement) +allJustMap elementToMaybeValue = + \array -> + array + |> Array.foldr + (\element soFar -> + case soFar of + Nothing -> + Nothing + + Just soFarList -> + Maybe.map + (\value -> soFarList |> (::) value) + (element |> elementToMaybeValue) + ) + ([] |> Just) + |> Maybe.map Array.fromList + + +{-| The first element (the one with lowest index) that returns a `Just` value for a given function. +If every element is transformed to `Nothing`, `Nothing` is returned. + +This is like a nicer [`any`](#any) where if you test for certain elements you already get one +that matches your search. + + import Array exposing (empty, fromList) + + empty + |> firstJustMap String.toInt + --> Nothing + + fromList [ "1", "2", "3" ] + |> firstJustMap String.toInt + --> Just 1 + + fromList [ "NaN", "2", "3" ] + |> firstJustMap String.toInt + --> Just 2 + +This is often called "findMap" +(for example in [`List.Extra`](https://package.elm-lang.org/packages/elm-community/list-extra/latest/List-Extra#findMap)). + +-} +firstJustMap : + (element -> Maybe narrowElement) + -> Array element + -> Maybe narrowElement +firstJustMap elementToMaybeValue = + \array -> + array + |> Array.foldl + (\element soFar -> + case soFar of + Just found -> + found |> Just + + Nothing -> + element |> elementToMaybeValue + ) + Nothing {-| Apply a given `Array` of changes to all elements. diff --git a/tests/Tests.elm b/tests/Tests.elm index ecf9822..dbb6061 100644 --- a/tests/Tests.elm +++ b/tests/Tests.elm @@ -889,6 +889,44 @@ suite = ) ) ] + , Test.describe "firstJustMap" + [ Test.fuzz (Fuzz.array (Fuzz.maybe Fuzz.int)) + "is equivalent to filterMap |> get 0" + (\maybes -> + maybes + |> Array.firstJustMap identity + |> Expect.equal + (maybes + |> Array.filterMap identity + |> Array.get 0 + ) + ) + ] + , Test.describe "allJustMap" + [ Test.fuzz (Fuzz.array (Fuzz.map Just Fuzz.int)) + "is equivalent to Just filterMap when only justs" + (\maybes -> + maybes + |> Array.allJustMap identity + |> Expect.equal + (maybes + |> Array.filterMap identity + |> Just + ) + ) + , Test.fuzz + (Fuzz.constant (\before after -> Array.append (before |> Array.push Nothing) after) + |> Fuzz.andMap (Fuzz.array (Fuzz.maybe Fuzz.int)) + |> Fuzz.andMap (Fuzz.array (Fuzz.maybe Fuzz.int)) + ) + "is equivalent to Nothing when one Nothing" + (\maybes -> + maybes + |> Array.allJustMap identity + |> Expect.equal + Nothing + ) + ] ]