langyu-app / elm-ancillary-nonempty-list / List.Nonempty.Ancillary

The List.Nonempty.Ancillary module provides additional convenience functions not found in mgold/elm-nonempty-list for dealing with non-empty lists.

Getting, Setting, and Updating

setAt : Basics.Int -> a -> List.Nonempty.Nonempty a -> List.Nonempty.Nonempty a

Given an index and a value, replace the element at that index with the given value. Indices are modulus the length of the list, so out-of-range errors cannot occur; this means that negative indices are supported, e.g. -1 to set the last element. This is consistent with the behavior of List.Nonempty.get

Note that this is not particularly efficient (iterating over the entire list multiple times) and should probably not be used for very large lists. Arrays are of course preferable in such cases.

import List.Nonempty exposing (Nonempty(..))

setAt 1 "er" <| Nonempty "foo" [ "bar", "baz" ]
--> Nonempty "foo" [ "er", "baz" ]

setAt 6 "yi" <| Nonempty "foo" [ "bar", "baz" ]
--> Nonempty "yi" [ "bar", "baz" ]

setAt -1 "san" <| Nonempty "foo" [ "bar", "baz" ]
--> Nonempty "foo" [ "bar", "san" ]

updateAt : Basics.Int -> (a -> a) -> List.Nonempty.Nonempty a -> List.Nonempty.Nonempty a

Given an index and an update function, replace the value at that index by calling the update function. Indices are modulus the length of the list, so out-of-range errors cannot occur; this means that negative indices are supported, e.g. -1 to update the last element. This is consistent with the behavior of List.Nonempty.get

Note that this is not particularly efficient (iterating over the entire list multiple times) and should probably not be used for very large lists. Arrays are of course preferable in such cases.

import List.Nonempty exposing (Nonempty(..))

updateAt 1 ((+) 1) <| Nonempty 1 [ 1, 1 ]
--> Nonempty 1 [ 2, 1 ]

updateAt 6 ((+) 1) <| Nonempty 1 [ 1, 1 ]
--> Nonempty 2 [ 1, 1 ]

updateAt -1 ((+) 1) <| Nonempty 1 [ 1, 1 ]
--> Nonempty 1 [ 1, 2 ]

Building

appendList : List.Nonempty.Nonempty a -> List a -> List.Nonempty.Nonempty a

Append a list to the end of a non-empty list.

import List.Nonempty exposing (Nonempty(..))

appendList (Nonempty 1 [ 2 ]) [ 3, 4 ]
--> Nonempty 1 [ 2, 3, 4 ]

appendList (Nonempty 1 [ 2 ]) []
--> Nonempty 1 [ 2 ]

prependList : List a -> List.Nonempty.Nonempty a -> List.Nonempty.Nonempty a

Prepend a list to the beginning of a non-empty list.

import List.Nonempty exposing (Nonempty(..))

prependList [ 1, 2 ] <| Nonempty 3 [ 4 ]
--> Nonempty 1 [ 2, 3, 4 ]

prependList [] <| Nonempty 1 [ 2 ]
--> Nonempty 1 [ 2 ]

initialize : Basics.Int -> (Basics.Int -> a) -> List.Nonempty.Nonempty a

Initialize a list of a given length by calling a function with each index. The resulting list will always have at least 1 item, even if called with a negative or zero length.

import List.Nonempty exposing (Nonempty(..))

initialize 1 (\i -> i)
--> Nonempty 0 []

initialize 3 (\i -> i * 2)
--> Nonempty 0 [ 2, 4 ]

initialize 0 (\i -> i + 1)
--> Nonempty 1 []

initialize -1 (\i -> i + 1)
--> Nonempty 1 []

Folds

foldr : (a -> b -> b) -> b -> List.Nonempty.Nonempty a -> b

Reduce a non-empty list from the right.

import List.Nonempty exposing (Nonempty(..))

foldr (+) 0 <| Nonempty 1 [ 2, 3 ]
--> 6

foldr (::) [] <| Nonempty 1 [ 2, 3 ]
--> [ 1, 2, 3 ]

foldr1 : (a -> a -> a) -> List.Nonempty.Nonempty a -> a

Reduce a non-empty list from the right, treating the last element of the list as the starting value.

import List.Nonempty exposing (Nonempty(..))

foldr1 (-) <| Nonempty 1 [ 2, 3, 4 ]
--> -2

foldr1 (++) <| Nonempty "a" [ "b", "c" ]
--> "abc"

indexedFoldl : (Basics.Int -> a -> b -> b) -> b -> List.Nonempty.Nonempty a -> b

foldl that also passes the index of the current element to the step function.

import List.Nonempty exposing (Nonempty(..))

indexedFoldl (\i x acc -> String.fromInt i ++ x ++ acc) "" <| Nonempty "a" [ "b", "c" ]
--> "2c1b0a"

indexedFoldr : (Basics.Int -> a -> b -> b) -> b -> List.Nonempty.Nonempty a -> b

foldr that also passes the index of the current element to the step function. Note that this isn't particularly efficient, as it traverses the list multiple times.

import List.Nonempty exposing (Nonempty(..))

indexedFoldr (\i x acc -> String.fromInt i ++ x ++ acc) "" <| Nonempty "a" [ "b", "c" ]
--> "0a1b2c"

Uniqueness

unique : List.Nonempty.Nonempty a -> List.Nonempty.Nonempty a

Remove all duplicate values from the non-empty list, keeping only the first instance of each element that appears more than once.

import List.Nonempty exposing (Nonempty(..))

unique <| Nonempty 0  [ 1, 1, 0, 1 ]
--> Nonempty 0 [ 1 ]

uniqueBy : (a -> b) -> List.Nonempty.Nonempty a -> List.Nonempty.Nonempty a

Remove all duplicate values from the non-empty list, where uniqueness is determined first by applying a function, keeping only the first instance of each element that appears more than once.

import List.Nonempty exposing (Nonempty(..))

uniqueBy (String.left 1) <| Nonempty "foo"  [ "bar", "baz", "fun", "boil" ]
--> Nonempty "foo" [ "bar" ]

Note that this only applies the function once for each element, in case it is an expensive operation.

allDifferent : List.Nonempty.Nonempty a -> Basics.Bool

Indicate whether or not the non-empty list has duplicate values.

import List.Nonempty exposing (Nonempty(..))

allDifferent <| Nonempty 0 [ 1, 1, 0, 1 ]
--> False

allDifferent <| Nonempty 0 [ 1, 2]
--> True

allDifferentBy : (a -> b) -> List.Nonempty.Nonempty a -> Basics.Bool

Indicate whether or not the non-empty list has duplicate values after transformation by some function.

import List.Nonempty exposing (Nonempty(..))

allDifferentBy (String.left 1) <| Nonempty "foo" [ "bar", "baz" ]
--> False

allDifferentBy (String.left 1) <| Nonempty "far" [ "bar", "car" ]
--> True

Note that this only applies the function once for each element, in case it is an expensive operation.

Finding Extrema

Find minimum/maximum elements without Maybes.

maximum : List.Nonempty.Nonempty comparable -> comparable

Find the maximum element in a non-empty list.

import List.Nonempty exposing (Nonempty(..))

maximum <| Nonempty 1 [ 3, 2 ]
--> 3

maximumBy : (a -> comparable) -> List.Nonempty.Nonempty a -> a

Given a function to map a type to a comparable type, find the first maximum element in a non-empty list.

import List.Nonempty exposing (Nonempty(..))

maximumBy (\i -> i * i) <| Nonempty 1 [ -3, 3 ]
--> -3

maximumWith : (a -> a -> Basics.Order) -> List.Nonempty.Nonempty a -> a

Given a comparison function, find the first maximum element in a non-empty list.

import List.Nonempty exposing (Nonempty(..))

Nonempty { id = 0, val = 1 } [ { id = 1, val = 3 }, { id = 2, val = 0 } ]
    |> maximumWith (\a b -> compare a.val b.val)
--> { id = 1, val = 3 }

minimum : List.Nonempty.Nonempty comparable -> comparable

Find the minimum element in a non-empty list.

import List.Nonempty exposing (Nonempty(..))

minimum <| Nonempty 1 [ 3, 2 ]
--> 1

minimumBy : (a -> comparable) -> List.Nonempty.Nonempty a -> a

Given a function to map a type to a comparable type, find the first minimum element in a non-empty list.

import List.Nonempty exposing (Nonempty(..))

minimumBy (\i -> i * i) <| Nonempty 1 [ -1, 2 ]
--> 1

minimumWith : (a -> a -> Basics.Order) -> List.Nonempty.Nonempty a -> a

Given a comparison function, find the first minimum element in a non-empty list.

import List.Nonempty exposing (Nonempty(..))

Nonempty { id = 0, val = 1 } [ { id = 1, val = 3 }, { id = 2, val = 0 } ]
    |> minimumWith (\a b -> compare a.val b.val)
--> { id = 2, val = 0 }

Finding Extrema With Indices

Find minimum/maximum elements and their indices without Maybes.

indexedMaximum : List.Nonempty.Nonempty comparable -> ( Basics.Int, comparable )

Find the first maximum element in a non-empty list and its index.

import List.Nonempty exposing (Nonempty(..))

indexedMaximum <| Nonempty 1 [ 3, 2, 3 ]
--> ( 1, 3 )

indexedMaximumBy : (a -> comparable) -> List.Nonempty.Nonempty a -> ( Basics.Int, a )

Given a function to map a type to a comparable type, find the first maximum element in a non-empty list and its index.

import List.Nonempty exposing (Nonempty(..))

indexedMaximumBy (\i -> i * i) <| Nonempty 1 [ -3, 3 ]
--> ( 1, -3 )

indexedMaximumWith : (a -> a -> Basics.Order) -> List.Nonempty.Nonempty a -> ( Basics.Int, a )

Given a comparison function, find the first maximum element in a non-empty list and its index.

import List.Nonempty exposing (Nonempty(..))

Nonempty { id = 0, val = 1 } [ { id = 1, val = 3 }, { id = 2, val = 3 } ]
    |> indexedMaximumWith (\a b -> compare a.val b.val)
--> ( 1, { id = 1, val = 3 } )

indexedMinimum : List.Nonempty.Nonempty comparable -> ( Basics.Int, comparable )

Find the first minimum element in a non-empty list and its index.

import List.Nonempty exposing (Nonempty(..))

indexedMinimum <| Nonempty 2 [ 2, 1, 1 ]
--> ( 2, 1 )

indexedMinimumBy : (a -> comparable) -> List.Nonempty.Nonempty a -> ( Basics.Int, a )

Given a function to map a type to a comparable type, find the first minimum element in a non-empty list and its index.

import List.Nonempty exposing (Nonempty(..))

indexedMinimumBy (\i -> i * i) <| Nonempty 2 [ -1, 1 ]
--> ( 1, -1 )

indexedMinimumWith : (a -> a -> Basics.Order) -> List.Nonempty.Nonempty a -> ( Basics.Int, a )

Given a comparison function, find the first minimum element in a non-empty list and its index.

import List.Nonempty exposing (Nonempty(..))

Nonempty { id = 0, val = 1 } [ { id = 1, val = 3 }, { id = 2, val = 1 } ]
    |> indexedMinimumWith (\a b -> compare a.val b.val)
--> ( 0, { id = 0, val = 1 } )

Searching

find : (a -> Basics.Bool) -> List.Nonempty.Nonempty a -> Maybe a

Find the first element that satisfies a predicate and return Just that element, or if none match, return Nothing.

import List.Nonempty exposing (Nonempty(..))

find (\i -> i > 3) <| Nonempty 2 [ 4, 6, 8 ]
--> Just 4

find (\i -> i > 3) <| Nonempty 0 [ 1, 2, 3 ]
--> Nothing

elemIndex : a -> List.Nonempty.Nonempty a -> Maybe Basics.Int

Return Just the index (starting from 0) of the first instance of the element. If the element does not exist in the list, return Nothing.

import List.Nonempty exposing (Nonempty(..))

elemIndex 1 <| Nonempty 1 [ 2, 3 ]
--> Just 0

elemIndex 4 <| Nonempty 1 [ 2, 3 ]
--> Nothing

elemIndex 1 <| Nonempty 1 [ 1, 7 ]
--> Just 0

elemIndices : a -> List.Nonempty.Nonempty a -> List Basics.Int

Return a (possibly empty) list of all indices (starting from 0) at which the element occurs.

import List.Nonempty exposing (Nonempty(..))

elemIndices 1 <| Nonempty 1 [ 2, 3 ]
--> [ 0 ]

elemIndices 4 <| Nonempty 1 [ 2, 3 ]
--> []

elemIndices 1 <| Nonempty 1 [ 1, 7 ]
--> [ 0, 1 ]

findIndex : (a -> Basics.Bool) -> List.Nonempty.Nonempty a -> Maybe Basics.Int

Given a predicate and a nonempty list, return Just the index (starting from 0) of the first element that satisfies the predicate. If no element in the list satisfies the predicate, return Nothing.

import List.Nonempty exposing (Nonempty(..))

findIndex (\i -> i == 7) <| Nonempty 1 [ 1, 7 ]
--> Just 2

findIndex (\i -> i < 1) <| Nonempty 1 [ 3, 5 ]
--> Nothing

findIndex (\i -> i > 1) <| Nonempty 1 [ 2, 3 ]
--> Just 1

findIndices : (a -> Basics.Bool) -> List.Nonempty.Nonempty a -> List Basics.Int

Given a predicate and a nonempty list, return a (possibly empty) list of all indices (starting from 0) at which the element satisfies the predicate.

import List.Nonempty exposing (Nonempty(..))

findIndices (\i -> i == 7) <| Nonempty 1 [ 1, 7 ]
--> [ 2 ]

findIndices (\i -> i < 1) <| Nonempty 1 [ 3, 5 ]
--> []

findIndices (\i -> i > 1) <| Nonempty 1 [ 2, 3 ]
--> [ 1, 2 ]

findMap : (a -> Maybe b) -> List.Nonempty.Nonempty a -> Maybe b

Apply a function that may succeed (return a Just value) to values in a non-empty list, returning the result of the first successful match. If none match, then return Nothing.

import List.Nonempty exposing (Nonempty(..))

findMap String.toInt <| Nonempty "a" [ "b", "3" ]
--> Just 3

findMap String.toInt <| Nonempty "a" [ "b", "c" ]
--> Nothing

count : (a -> Basics.Bool) -> List.Nonempty.Nonempty a -> Basics.Int

Return the number of elements in the list that satisfy a given predicate.

import List.Nonempty exposing (Nonempty(..))

count ((==) Nothing << String.toInt) <| Nonempty "1" [ "yi", "2", "er", "3", "san" ]
--> 3

Maybes

combine : List.Nonempty.Nonempty (Maybe a) -> Maybe (List.Nonempty.Nonempty a)

If every Maybe in the non-empty list is Just a, then return a list of all the unwrapped values. If one or more elements are Nothing, then the entire output will be Nothing.

import List.Nonempty exposing (Nonempty(..))

combine (Nonempty (Just 1) [ Just 2, Just 3 ])
--> Just (Nonempty 1 [ 2, 3 ])

combine (Nonempty (Just 1) [ Nothing, Just 3 ])
--> Nothing

traverse : (a -> Maybe b) -> List.Nonempty.Nonempty a -> Maybe (List.Nonempty.Nonempty b)

Map a function over every element in the non-empty list. If every function call returns Just a, then return a list of all the values. If one or more function call returns Nothing, then the entire output will be Nothing.

import List.Nonempty exposing (Nonempty(..))

traverse List.head (Nonempty [ 1 ] [ [2], [ 3, 4 ] ])
--> Just (Nonempty 1 [ 2, 3 ])

traverse List.head (Nonempty [ 1 ] [ [], [ 3, 4 ] ])
--> Nothing

JSON Decoders/Encoders

The module provides two sets of JSON decoder/encodes. In the first, decodeArray/encodeArray, the non-empty list is represented as a standard JSON array. Decoding an empty array will fail, and encoding to an empty array will never happen.

In the second, decodeObject/encodeObject, the non-empty list is explicitly represented as an object with two keys: "head" (containing the first element) and "tail", containing a (possibly empty) array of the rest of the elements:

{ "head": x1
, "tail": [x2, x3]
}

decodeArray : Json.Decode.Decoder a -> Json.Decode.Decoder (List.Nonempty.Nonempty a)

Decode a non-empty list from a JSON array, failing if it is empty.

import List.Nonempty exposing (Nonempty(..))
import Json.Decode as Decode

Decode.decodeString (decodeArray Decode.int) "[1,2,3]"
--> Ok (Nonempty 1 [ 2, 3 ])
Decode.decodeString (decodeArray Decode.int) "[]"
    |> Result.toMaybe
--> Nothing

encodeArray : (a -> Json.Encode.Value) -> List.Nonempty.Nonempty a -> Json.Encode.Value

Turn a non-empty list into a JSON array.

import List.Nonempty exposing (Nonempty(..))
import Json.Encode as Encode

Encode.encode 0 (encodeArray Encode.int <| Nonempty 1 [ 2, 3 ])
--> "[1,2,3]"

decodeObject : Json.Decode.Decoder a -> Json.Decode.Decoder (List.Nonempty.Nonempty a)

Decode a non-empty list from a JSON object of the form:

{ "head": x1
, "tail": [x2, x3]
}


import List.Nonempty exposing (Nonempty(..))
import Json.Decode as Decode

Decode.decodeString (decodeObject Decode.int) "{ \"head\": 1, \"tail\": [2,3] }"
--> Ok (Nonempty 1 [ 2, 3 ])

encodeObject : (a -> Json.Encode.Value) -> List.Nonempty.Nonempty a -> Json.Encode.Value

Encode a non-empty list into a JSON object of the form:

{ "head": x1
, "tail": [x2, x3]
}

import List.Nonempty exposing (Nonempty(..))
import Json.Encode as Encode

Encode.encode 0 (encodeObject Encode.int <| Nonempty 1 [ 2, 3 ])
--> "{\"head\":1,\"tail\":[2,3]}"

Random

generator : Basics.Int -> Random.Generator a -> Random.Generator (List.Nonempty.Nonempty a)

Generate a non-empty list of a specified length. If the length is less than 1, the resultant list will be of length 1.

For instance, generator 1 g, generator 0 g and generator -1 g will all produce a non-empty list of length 1, filled with a value generated by g.

shuffle : List.Nonempty.Nonempty a -> Random.Generator (List.Nonempty.Nonempty a)

Shuffle the non-empty list. This of course has no effect on a list of one element.

sequenceGenerators : List.Nonempty.Nonempty (Random.Generator a) -> Random.Generator (List.Nonempty.Nonempty a)

Given a non-empty list random generators, turn them into a generator that returns a non-empty list.