Search code examples
listrecursionelm

How to number list elements in Elm?


Given the following list

  List Records

where Record is

   type Record
   = RecordA A
   | RecordB B

and A and B are type aliases with pos field:

  type alias A =
  { pos : Maybe Int
  , type : Char
  }

how would I create a new list that numbers subsequent A and B records? I want to have pos = 1 in the first A record, pos = 2 in the second A record, pos = 1 in the first B record, etc. (The original list comes without numbers in the pos field.)

This is a solution for a language that allows mutation.

let countA = 0;
let countB = 0;
for (el of my_array) {
    el.pos = (el.type == 'A') ? ++countA : ++countB;
}

Solution

  • You can convert any iterative algorithm using mutable variables into a recursive one that don't quite easily by just making the mutable variables function arguments:

    enumerate : List Record -> Int -> Int -> List Record
    enumerate list countA countB =
        case list of
            [] ->
                []
    
            (RecordA el) :: rest ->
                RecordA { el | pos = Just countA } :: enumerate rest (countA + 1) countB
    
            (RecordB el) :: rest ->
                RecordB { el | pos = Just countB } :: enumerate rest countA (countB + 1)
    

    This is not tail recursive however, and will therefore overflow the stack on larger lists. It's also a bit inconvenient to have to specify the initial counts every time we use this. We can solve both by using an inner function and adding an accumulator argument to it:

    enumerate : List Record -> List Record
    enumerate list =
        let
            aux els acc posA posB =
                case list of
                    [] ->
                        acc
    
                    (RecordA el) :: rest ->
                        aux rest (RecordA { el | pos = Just posA } :: acc) (posA + 1) posB
    
                    (RecordB el) :: rest ->
                        aux rest (RecordB { el | pos = Just posB } :: acc) posA (posB + 1)
        in
        aux list [] 0 0
    

    You seem to have some deeper data modeling issues here though, but it also doesn't look like your actual types, so hopefully you'll figure that out eventually too. This should at least get you a bit closer.