Up to Main Index                         Up to Journal for September, 2022

                   JOURNAL FOR FRIDAY 30TH SEPTEMBER, 2022
______________________________________________________________________________

SUBJECT: Exploring maps
   DATE: Fri 30 Sep 20:32:31 BST 2022

It’s been a little quiet here as I’ve not had much time for coding or writing.
The time I did have, since releasing Mere v0.0.4[1], was spent implementing
maps. This post took way longer to write than usual, as I only wanted to show
actual working code for the examples and I kept breaking things. I also went
and changed the syntax for maps half way through writing this…

The biggest stumbling block has been the fact that in Go you cannot take the
address of a map element. Otherwise, I’d have used the same technique I used
for slices which worked really well. It would have made things much simpler.

Anyway, after many iterations, I nearly have maps working. In Mere, arrays are
defined as ‘[]type’ where ‘type’ is the type of the array entry, indexes are
always of type int. Maps are defined as ‘[type]’ where ‘type’ is the type of
the map index and map entries can be of any type. Some examples:


  inv:[string] = [string] "item" "Apple" "qty" 25 "price" 0.25

  println "inv: ", inv
  println
  printf "Item name:\t%s\t%s\n",  inv["item"],  type inv["item"]
  printf "Quantity:\t%d\t%s\n",   inv["qty"],   type inv["qty"]
  printf "Price:\t\t%4.2f\t%s\n", inv["price"], type inv["price"]


Here ‘inv’ is a map with string index values. It has 3 entries: item, qty and
price. When run the code produces:


  inv: map[item:Apple price:0.25 qty:25]

  Item name:  Apple  string
  Quantity:      25  int
  Price:       0.25  float


A new built-in ‘keys’ has been added to retrieve all the map indexes. For
example to iterate over a map:


  inv:[string] = [string] "item" "Apple" "qty" 25 "price" 0.25

  k:[]string = keys inv
  kloop:
    println k[0] ":\t" inv[k[0]]
    delete k 0
  if len(k) > 0; goto kloop


When run, the code produces:


  item:   Apple
  price:  0.25
  qty:    25


As maps can contain any type, maps can contain maps and slices. In the next
example an alternative way to create and populate a map is used, this time
each entry is a map with string indexes. The commas in the maps for each entry
are used to highlight the map key/value pairs, but are not required:


  fruits:[string]
  fruits["apple"]  = [string] "item" "Apple",  "qty"  25, "price" 0.25
  fruits["orange"] = [string] "item" "Orange", "qty"  37, "price" 0.30
  fruits["mango"]  = [string] "item" "Mango",  "qty" 101, "price" 1.25

  // Some temporary work variables
  fruit:[string]; total:float; cost:float
  line:string = "% 6s: %3d × %4.2f = %6.2f\n"

  k:[]string = keys fruits
  kloop:
    fruit = fruits[k[0]]
    total += cost = float fruit["qty"] * fruit["price"]

    printf line fruit["item"], fruit["qty"], fruit["price"], cost
    delete k 0
  if len(k) > 0; goto kloop

  printf "%27s\n"        "======"
  printf "%19s: %6.2f\n" "Total" total


Yes, I really did code “total += cost = float fruit…” ;) When run, the code
produces the following output:


   Apple:  25 × 0.25 =   6.25
   Mango: 101 × 1.25 = 126.25
  Orange:  37 × 0.30 =  11.10
                       ======
                Total: 143.60


As can be seen from above, Indexing into maps with nested maps, slices or
strings works as expected.

For each map type it is possible to create a map from a slice of the same
type, each pair of slice elements corresponding to a key/value map pair:


  println [int]    []int    1 1 2 10 3 11
  println [float]  []float  1.1 1.0 2.2 2.0
  println [string] []string "a" "A" "b" "B"
  println [bool]   []bool   false true true false


Would produce:


  map[1:1 2:10 3:11]
  map[1.1:1 2.2:2]
  map[a:A b:B]
  map[false:true true:false]


While the instance of using a string slice is handy, the others were added for
completeness. However, I’m not sure if this feature is really useful — I might
drop it before the next release.

Similar to slices, maps in Mere can be concatenated together and multiple
entries appended — to nitpick here, in the case of maps, entries are actually
added and not appended but the similarity works. For example:


  f1:[string] = [string] "apple"  [string] "qty" 25, "price" 0.25
  f2:[string] = [string] "orange" [string] "qty" 37, "price" 0.30
  f3:[string] = [string] f1, f2
  f3:[string] = [string] f3, ("mango"  [string] "qty" 101, "price" 1.25),
                             ("banana" [string] "qty"  78, "price" 0.97)

  println f3


Here f1 and f2 are maps with string indexes. We then create f3 and populate it
by concatenating f1 and f2 together. Then we add two new entries to f3. The
final output produced, manually line-wrapped for clarity, is:


  map[
    apple:map[price:0.25 qty:25]
    banana:map[price:0.97 qty:78]
    mango:map[price:1.25 qty:101]
    orange:map[price:0.3 qty:37]
  ]


Note the second [string] in the first four lines of code create an anonymous
map that is then allocated to f1, f2 and f3. On the fourth line this means f3
is allocated to the anonymous map, then the entries are added and the new map
allocated back to f3. For large maps this technique is less efficient than
using f3["mango"] and f3["banana"] to add the map entries. Something I will
have to look at optimizing.

I have all of the above examples working. There are a few iffy edge cases I’m
sorting out. I then need to update all of the operators and built-ins to
handle maps and map references where applicable — so far I’ve only updated a
few to prove my map concept implementation will work. After that I’ll push out
a Mere v0.0.5 for people to play with.

I’d like to take a minute to thank everybody who has taken the time to play
with Mere and to provide comments and feedback. It’s much appreciated. If you
have any further thoughts, maybe on the maps, maybe not: diddymus@wolfmud.org

--
Diddymus

  [1] ../../../annex


  Up to Main Index                         Up to Journal for September, 2022