turboMaCk / non-empty-list-alias / List.NonEmpty


type alias NonEmpty a =
( a, List a )

NonEmpty list is an alias for a pair of a and List a.

This makes it possible to construct value of non empty List without relying on any specific implementation of this type.

Create

singleton : a -> NonEmpty a

Creates NonEmpty list with only one element.

singleton 1
--> ( 1, [] )

cons : a -> NonEmpty a -> NonEmpty a

Add element to the beginning of NonEmpty list.

cons 2 ( 1, [] )
--> (2, [ 1 ])

fromList : List a -> Maybe (NonEmpty a)

Converts List to Maybe NonEmpty.

fromList [ 1, 2 ]
--> Just ( 1, [ 2 ] )

fromList []
--> Nothing

fromCons : a -> List a -> NonEmpty a

Cons element onto List to create NonEmpty.

fromCons 0 [ 1, 2 ]
--> (0, [1, 2])

This function is just an alias for Tuple.pair

unfoldl : (a -> ( b, Maybe a )) -> a -> NonEmpty b

Create NonEmpty by unfolding other data from left.

This is more expert way of constructing NonEmpty. It's useful in rare cases.

stepPrev : Int -> (String, Maybe Int)
stepPrev n =
    ( String.fromInt n
    , if n > 0 then
        Just (n - 1)
      else
         Nothing
     )

unfoldl stepPrev 5
--> ("0", ["1","2","3","4", "5"])

unfoldr : (a -> ( b, Maybe a )) -> a -> NonEmpty b

Create NonEmpty by unfolding other data from right.

This is more expert way of constructing NonEmpty. It's useful in rare cases.

stepNext : Int -> (String, Maybe Int)
stepNext n =
    ( String.fromInt n
    , if n < 5 then
        Just (n + 1)
      else
         Nothing
     )

unfoldr stepNext 0
--> ("0", ["1","2","3","4", "5"])

Transform

map : (a -> b) -> NonEmpty a -> NonEmpty b

Map a function over NonEmpty list.

map (\x -> x + 1) ( 1, [ 2, 3 ] )
--> ( 2, [3, 4] )

map String.fromInt ( 1, [ 2 ] )
--> ( "1", [ "2" ] )

indexedMap : (Basics.Int -> a -> b) -> NonEmpty a -> NonEmpty b

Same as map but an index is passed with each element.

Index starts at 0.

indexedMap (\i x -> String.fromInt i ++ " is " ++ x) ("a", ["b", "c"])
--> ("0 is a",["1 is b","2 is c"])

foldl : (a -> b -> b) -> b -> NonEmpty a -> b

Reduce NonEmpty from left.

foldl (+) 0 (1, [2,3,4])
--> 10

foldl cons (0, []) (1, [2,3,4])
--> (4, [3,2,1,0])

foldl1 : (a -> a -> a) -> NonEmpty a -> a

Collapse NonEmpty a into a value from left

foldl1 (+) (1, [2,3,4])
--> 10

foldl1 (++) ("hello", [" ","world"])
--> "world hello"

foldr : (a -> b -> b) -> b -> NonEmpty a -> b

Reduce NonEmpty from right.

foldr (+) 0 (1, [2,3,4])
--> 10

foldr cons (5, []) (1, [2,3,4])
--> (1, [2, 3, 4, 5])

foldr1 : (a -> a -> a) -> NonEmpty a -> a

Collapse NonEmpty a into a value from right.

foldr1 (+) (1, [2,3,4])
--> 10

foldr1 (++) ("hello", [" ","world"])
--> "hello world"

filter : (a -> Basics.Bool) -> NonEmpty a -> Maybe (NonEmpty a)

Keep elements that satisfy the test

isEven : Int -> Bool
isEven n = (n |> modBy 2) == 0

filter isEven (1,[2,3,4,5])
--> Just (2, [4] )

filterMap : (a -> Maybe b) -> NonEmpty a -> Maybe (NonEmpty b)

Apply function to each element of NonEmpty and leave out values that result in Nothing.

filterMap String.toInt ("1", ["baz", "3rd", "4"])
--> Just (1, [4])

filterMap String.toInt ("foo", ["baz", "3rd"])
--> Nothing

partition : (a -> Basics.Bool) -> NonEmpty a -> ( List a, List a )

Partition a non empty list on some test. The first list contains all values that satisfy the test, and the second list contains all the values that do not.

isEven : Int -> Bool
isEven n = (n |> modBy 2) == 0

partition isEven (0,[1,2,3,4,5])
--> ([0,2,4], [1,3,5])

Utilities

length : NonEmpty a -> Basics.Int

Calculate length of NonEmpty list.

length ( 1, [ 2, 3 ] )
--> 3

length ( 1, [] )
--> 1

reverse : NonEmpty a -> NonEmpty a

Reverse NonEmpty list.

reverse (1, [2, 3, 4])
--> (4, [3, 2, 1])

member : a -> NonEmpty a -> Basics.Bool

Figure out whether a NonEmpty list contains a value.

member 2 ( 1, [ 2 ] )
--> True

member 3 ( 1, [ 2 ] )
--> False

all : (a -> Basics.Bool) -> NonEmpty a -> Basics.Bool

Determine if all elements satisfy the test.

all Char.isUpper ( 'A', [ 'B' ] )
--> True

all Char.isUpper ( 'a', [ 'B' ] )
--> False

any : (a -> Basics.Bool) -> NonEmpty a -> Basics.Bool

Determine if any elements satisfies the test.

any Char.isUpper ( 'a', [ 'B' ] )
--> True

any Char.isUpper ( 'a', [ 'b' ] )
--> False

maximum : NonEmpty comparable -> comparable

Find the maximum element.

maximum ( 3, [ 3, 5, 2 ] )
--> 5

minimum : NonEmpty comparable -> comparable

Find the minimum element.

minimum ( 3, [ 3, 5, 2 ] )
--> 2

sum : NonEmpty number -> number

Get the sum of the list elements.

sum ( 2, [ 2, 2 ] )
--> 6

product : NonEmpty number -> number

Get the product of the list elements.

product ( 2, [ 2, 2 ] )
--> 8

last : NonEmpty a -> a

Returns last element of the NonEmpty.

last ( 1, [ 2 ] )
--> 2

last ( 1, [] )
--> 1

This function is O(n)

find : (a -> Basics.Bool) -> NonEmpty a -> Maybe a

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

find (\num -> num > 5) (2, [ 4, 6, 8 ])
--> Just 6

unique : NonEmpty a -> NonEmpty a

Remove duplicates of a NonEmpty list.

unique (0,[1,0,1,1,0])
--> (0,[1])

Combine

append : NonEmpty a -> NonEmpty a -> NonEmpty a

Put two lists together.

append ( 1, [ 2, 3 ] ) ( 4, [ 5 ] )
--> ( 1, [ 2, 3, 4, 5 ] )

concat : NonEmpty (NonEmpty a) -> NonEmpty a

Concatenate a bunch of lists into a single list.

concat ((1, [2, 3]), [(4, [5, 6]), (7, [8]), (9, []), (10, [11])])
--> (1,[2,3,4,5,6,7,8,9,10,11])

concatMap : (a -> NonEmpty b) -> NonEmpty a -> NonEmpty b

Map a given function onto a list and flatten the resulting lists.

concatMap singleton ( 1, [ 2 ] )
-->  ( 1, [ 2 ] )

concatMap (\x -> ( x + 1, [ x + 1 ] )) ( 1, [ 2 ] )
--> ( 2, [ 2, 3, 3 ] )

intersperse : a -> NonEmpty a -> NonEmpty a

Places the given value between all members of given list.

intersperse "and" ( "1", [ "2", "3" ] )
--> ("1", ["and", "2", "and", "3"])

intersperse "and" ( "1", [ "2" ] )
--> ("1", ["and", "2"])

intersperse "and" ( "1", [] )
--> ("1", [])

map2 : (a -> b -> c) -> NonEmpty a -> NonEmpty b -> NonEmpty c

Combine two lists with a given function. In case where one of the two lists is longer the extra elements are ignored.

map2 (+) ( 1, [ 2 ] ) ( 1, [ 1 ] )
--> (2, [3])

map2 (+) ( 1, [] ) ( 1, [ 1 ] )
--> (2, [])

map2 (+) ( 1, [ 1 ] ) ( 1, [] )
--> (2, [])

map2 Tuple.pair ( 1, [ 2, 3 ]) ("foo", [ "bar" ])
--> ( ( 1, "foo"), [ ( 2, "bar" ) ] )

andMap : NonEmpty a -> NonEmpty (a -> b) -> NonEmpty b

Map over multiple NonEmpty lists.

map (+) (1, [2])
|> andMap (1, [1])
--> (2, [3])

type alias User =
    { name : String
    , age : Int
    , admin : Bool
    }

( User, [ User, User ] )
|> andMap ( "Alice", [ "Bob", "Charlie"] )
|> andMap ( 30, [ 50, 19 ] )
|> andMap ( True, [ False, False ])
--> ( User "Alice" 30 True
--> , [ User "Bob" 50 False, User "Charlie" 19 False ]
--> )

Sort

sort : NonEmpty comparable -> NonEmpty comparable

Sort values from lowest to highest.

sort ( 3, [ 4, 1, 2 ] )
--> (1, [2, 3, 4])

sortBy : (a -> comparable) -> NonEmpty a -> NonEmpty a

Sort values by a derived property.

sortBy String.length ( "333", [ "4444", "1", "22" ] )
--> ("1", ["22", "333", "4444"])

sortWith : (a -> a -> Basics.Order) -> NonEmpty a -> NonEmpty a

Sort values with a custom comparison function.

Deconstruct

isSingleton : NonEmpty a -> Basics.Bool

Is the nonempty list exactly one element?

isSingleton ( 1, [] )
--> True

isSingleton ( 1, [ 2 ] )
--> False

head : NonEmpty a -> a

Returns first element of the NonEmpty.

head ( 1, [ 2 ] )
--> 1

tail : NonEmpty a -> List a

Returns tail of the NonEmpty. The return type is List and may be empty.

tail ( 1, [ 2, 3 ] )
--> [2, 3]

If you're looking for function that produces another NonEmpty see dropHead

take : Basics.Int -> NonEmpty a -> NonEmpty a

Take the first n elements of NonEmpty.

take 2 ( 1, [ 2, 3, 4 ] )
--> ( 1, [ 2 ] )

take 0 ( 1, [ 2, 3, 4 ] )
--> ( 1, [] )

dropHead : NonEmpty a -> Maybe (NonEmpty a)

Removes first element of the NonEmpty list.

dropHead ( 1, [ 2 ] )
--> Just (2, [])

dropHead ( 1, [] )
--> Nothing

drop : Basics.Int -> NonEmpty a -> Maybe (NonEmpty a)

Drop the first n elements of NonEmpty list.

drop 2 ( 1, [ 2, 3, 4 ] )
--> Just ( 3, [4] )

uncons : NonEmpty a -> ( a, Maybe (NonEmpty a) )

Remove first element form NonEmpty list.

uncons ( 3, [ 2, 1 ] )
--> ( 3, Just ( 2, [ 1 ] ) )

uncons ( "hello!", [] )
--> ( "hello!", Nothing )

toList : NonEmpty a -> List a

Converts NonEmpty to List.

toList ( 1, [ 2 ] )
--> [1, 2]

Expand

duplicate : NonEmpty a -> NonEmpty (NonEmpty a)

Create NonEmpty containing sub NoneEmpty lists.

This is a more advanced function following Comonad

duplicate ( 1, [ 2, 3 ] )
--> ( ( 1, [ 2, 3 ] ), [ ( 2, [ 3 ] ), ( 3, [] ) ] )

duplicate ( "alone", [] )
--> ( ( "alone", [] ), [] )

extend : (NonEmpty a -> b) -> NonEmpty a -> NonEmpty b

Map value to a new value based on tail of a list.

This is a more advanced function following Comonad

-- for each element sum all elements till the end
extend sum ( 1, [ 2, 3 ] )
--> ( 6, [ 5, 3 ] )

-- calculate length at each point of NonEmpty list
extend length ("foo", [ "bar", "baz", "EOF"] )
--> ( 4, [ 3, 2, 1 ])

JSON

decodeList : Json.Decode.Decoder a -> Json.Decode.Decoder (NonEmpty a)

Decode JSON array to NonEmpty.

import Json.Decode as JD exposing (Decoder)
import Json.Encode as JE

strings : Decoder (NonEmpty String)
strings =
    decodeList JD.string

JD.decodeString strings "[\"foo\",\"bar\",\"baz\"]"
--> Ok ( "foo", [ "bar", "baz" ])

JD.decodeString strings "[]"
--> Err <| JD.Failure "Expecting at least ONE ELEMENT array" <|
-->     JE.list identity []

JD.decodeString strings "{}"
--> Err <| JD.Failure "Expecting a LIST" <|
-->      JE.object []

decode : Json.Decode.Decoder (a -> List a -> NonEmpty a)

Helper for creating custom Decoder.

import Json.Decode as JD exposing (Decoder)
import Json.Encode as JE
import Json.Decode.Extra as JDE
import Json.Decode.Pipeline as JDP

-- Decoding from custom object

objectDecoder : Decoder (NonEmpty Int)
objectDecoder =
    decode
     |> JDP.required "head" JD.int
     |> JDP.required "tail" (JD.list JD.int)


JD.decodeString objectDecoder "{\"head\":1,\"tail\":[2,3]}"
--> Ok (1, [ 2, 3 ])

JD.decodeString objectDecoder "{\"head\":true}"
--> Err <| JD.Failure "Expecting an OBJECT with a field named `tail`" <|
-->     JE.object [ ("head", JE.bool True) ]

-- Decoding from Array of Arrays

nestedArrayDecoder : Decoder (NonEmpty Bool)
nestedArrayDecoder =
    decode
    |> JDE.andMap (JD.index 0 JD.bool)
    |> JDE.andMap (JD.index 1 <| JD.list JD.bool)

JD.decodeString nestedArrayDecoder "[true, [false, true]]"
--> Ok (True, [False, True])

JD.decodeString nestedArrayDecoder "[false]"
--> Err <| JD.Failure "Expecting a LONGER array. Need index 1 but only see 1 entries" <|
-->     JE.list JE.bool [False]

encodeList : (a -> Json.Encode.Value) -> NonEmpty a -> Json.Encode.Value

Encode NonEmpty as JSON array.