Jens Egholm
Jens Egholm

Reputation: 2710

Iterating through record fields

In Elm, one of my records (the type alias construct) have a lot of entries, and I was wondering if there's a built-in way to iterate through it. Either directly or by converting it to a Dict

So I was thinking something like:

let
  myRecord = MyRecord ...
  showEntry key value = ...
in
  map showEntry myRecord

Thanks for your time!

Update

To answer the questions about the actual data, I have 12 fields of the same type. The 12 fields are all required to have that exact name, hence the record type and not a Dict. Whether it's named as a string in a record or a type in itself doesn't matter, as long as I can uniquely identify that value from the other 11. In code the record looks something like:

type alias InnerType { value : Int, ... }

type alias Record = { inner1 : InnerType, inner2 : InnerType, ... }

Since all the fields in the record have the same type, I just wanted to see if there's an easier way to go through them instead of naming all 12. Unless there's a better way to represent this, in which case I'm all ears! :-)

Upvotes: 1

Views: 1125

Answers (2)

Chad Gilbert
Chad Gilbert

Reputation: 36385

If your record will only ever have twelve elements and you don't mind sacrificing a little verbosity in your type definition for type safety (make impossible states impossible!), you could start with enumeration of those twelve indexes:

type Index
    = Inner1
    | Inner2
    ...
    | Inner12

You could redefine Record to a type instead of a record type alias:

type Record =
    Record
        InnerType
        InnerType
        -- repeat InnerType 12 times

Now you can create a few convenience functions for getting and setting values in a type-safe manner:

get : Index -> Record -> InnerType
get i (Record i1 i2 i3 i4 i5 i6 i7 i8 i9 i10 i11 i12) =
    case i of
        Inner1 -> i1
        Inner2 -> i2
        ...
        Inner12 -> i12

set : Index -> InnerType -> Record -> Record
set i val (Record i1 i2 i3 i4 i5 i6 i7 i8 i9 i10 i11 i12) =
    case i of
        Inner1 -> Record val i2 i3 i4 i5 i6 i7 i8 i9 i10 i11 i12
        Inner2 -> Record i1 val i3 i4 i5 i6 i7 i8 i9 i10 i11 i12
        ...
        Inner12 -> Record i1 i2 i3 i4 i5 i6 i7 i8 i9 i10 i11 val

And you can create a list from your record like this:

toList : Record -> List InnerType
toList (Record i1 i2 i3 i4 i5 i6 i7 i8 i9 i10 i11 i12) =
    [ i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12 ]

As you can see, this gets verbose, but it's only in the definition of the Record type that it becomes verbose. If you confine all this code to a Record module, then accessing and modifying records is clean, concise, and more importantly, type safe. You are not subject to a Dictionary where a key may not exist, or a List or Array where an index might not exist. Your Record type would be sealed up nice and tight, at the expense of a little verbosity in the definition.

Here is a gist containing the full definition.

Upvotes: 5

Simon H
Simon H

Reputation: 21037

Looks like you need a List (or an Array) of InnerType, as you said the structure itself is fixed at 12 of these. As for iterating, here's the example for json encoding a list of InnerType

inners 
  |> List.indexedMap (\idx inner -> ("inner" ++ toString idx, encodeInner inner))
  |> Json.Encode.object 

Upvotes: 2

Related Questions