NB. [nested] map of (,: key;value) entries
NB.
NB. INSTALL
NB.    > copy map.ijs user/
NB.
NB. RUN
NB.   require '~user/map.ijs'
NB.   coinsert 'map'
NB.
NB.
NB. FLAT MAPS
NB.
NB. creating a map:
NB.
NB.   MAP=: empty''                      NB. you should've guessed!
NB.   MAP=: 1 2 3 'one' setmap empty''   NB. with some values
NB.   MAP=: ('k1';i.3),:('k2';'qq')      NB. declaratively
NB. +--+-----+
NB. |k1|0 1 2|
NB. +--+-----+
NB. |k2|qq   |
NB. +--+-----+
NB.
NB. changing values:
NB.
NB.   MAP=: (i.3 4) 'k2' setmap MAP      NB. similar to amend, "}"
NB. +--+---------+
NB. |k1|0 1 2    |
NB. +--+---------+
NB. |k2|0 1  2  3|
NB. |  |4 5  6  7|
NB. |  |8 9 10 11|
NB. +--+---------+
NB.
NB. adding values:
NB.
NB.   MAP=: 'qq' 'k3' setmap MAP         NB. same as setting
NB. +--+---------+
NB. |k1|0 1 2    |
NB. +--+---------+
NB. |k2|0 1  2  3|
NB. |  |4 5  6  7|
NB. |  |8 9 10 11|
NB. +--+---------+
NB. |k3|qq       |
NB. +--+---------+
NB.
NB. removing entries:
NB.                                NB. i.e. setting to nothing
NB.   MAP=: 'k2' setmap MAP        NB. graceful for missing keys
NB. +--+---------+
NB. |k1|0 1 2    |
NB. +--+---------+
NB. |k3|qq       |
NB. +--+---------+
NB.
NB. getting values:
NB.                                NB. similar to From '{'
NB.   VALUE=: 'k1' getmap MAP      NB. missing keys gracefully return empty''
NB. 0 1 2
NB.
NB.    getmap MAP                 NB. catalog of keys, like '{'
NB. +--+--+--+
NB. |k1|k2|k3|
NB. +--+--+--+
NB.
NB. index of the key:
NB.
NB.   INDEX=: 'k4' ndxmap MAP
NB.
NB. existance of key:
NB.
NB.   BOOL=: 'k4' hasmap MAP
NB.
NB.
NB. NESTED MAPS
NB.
NB. build a nested map:
NB.
NB.   MAPX=: 3 'k2.k6' setmapx 'val' 'k2.k4' setmapx 1 'k1.k3' setmapx empty''
NB.   MAPX=: 3 'k2.k4.k8' setmapx (i.3 4) 'k1.k5' setmapx MAPX
NB. +--+--------------+
NB. |k1|+--+---------+|
NB. |  ||k3|1        ||
NB. |  |+--+---------+|
NB. |  ||k5|0 1  2  3||
NB. |  ||  |4 5  6  7||
NB. |  ||  |8 9 10 11||
NB. |  |+--+---------+|
NB. +--+--------------+
NB. |k2|+--+--------+ |
NB. |  ||k4|+--+---+| |
NB. |  ||  ||  |val|| |
NB. |  ||  |+--+---+| |
NB. |  ||  ||k8|3  || |
NB. |  ||  |+--+---+| |
NB. |  |+--+--------+ |
NB. |  ||k6|3       | |
NB. |  |+--+--------+ |
NB. +--+--------------+
NB.
NB. removing nested entries:
NB.                                NB. i.e. setting to nothing
NB.    'k1.k5' setmapx 'k2' setmapx MAPX
NB. +--+------+
NB. |k1|+--+-+|
NB. |  ||k3|1||
NB. |  |+--+-+|
NB. +--+------+
NB.
NB. value of deep key from nested map:
NB.
NB.    'k1.k3' getmapx MAPX
NB. 1
NB.    'k2.k4' getmapx MAPX
NB. +--+---+
NB. |  |val|
NB. +--+---+
NB. |k8|3  |
NB. +--+---+
NB.    'k2.k4.' getmapx MAPX
NB. val
NB.
NB.    getmapx MAPX               NB. catalog of nested keys, like '{'
NB. +-----+-----+------+--------+-----+
NB. |k1.k3|k1.k5|k2.k4.|k2.k4.k8|k2.k6|
NB. +-----+-----+------+--------+-----+
NB.
NB.    flatmap MAPX                NB. covert nested map to flat
NB. +--------+---------+           NB. like Spread 0
NB. |k1.k3   |1        |
NB. +--------+---------+
NB. |k1.k5   |0 1  2  3|
NB. |        |4 5  6  7|
NB. |        |8 9 10 11|
NB. +--------+---------+
NB. |k2.k4.  |val      |
NB. +--------+---------+
NB. |k2.k4.k8|3        |
NB. +--------+---------+
NB. |k2.k6   |3        |
NB. +--------+---------+
NB.
NB.    MAPX -: flatmapx flatmap MAPX      NB. reverse of flattening
NB. 1
NB.
NB.    strmap MAPX        NB. covert nested map to multiline string
NB. k1.k3    1            NB. useful for storing and restoring config
NB. k1.k5    i.3 4
NB. k2.k4.   'val'
NB. k2.k4.k8 3
NB. k2.k6    3
NB.
NB.    MAPX -: strmapx strmap MAPX        NB. reverse from string
NB. 1
NB.
NB. CMAP=: 0 : 0                  NB. build nested map from COMPACT string
NB. k1 .k3  1     .k5    i.3 4
NB. k2 .k4. 'val' .k4.k8 3     .k6 3
NB. )
NB.    MAPX -: strmapc CMAP
NB. 1
NB.
NB. AUTHOR
NB.    (C) Oleg Kobchenko <olegykj@yahoo.com>, 10/12/2003
NB.        GPL, AS-IS, NO WARRANTY
NB.
NB.    10/23/2003 added strmap[x|c]


coclass 'map'

NB.*ndxmap v index of the key
NB. INDEX=: 'key' ndxmap MAP

ndxmap=: (i. boxopen)~ getmap


NB.*hasmap v existance of the key
NB. BOOL=: 'key' hasmap MAP

hasmap=: ndxmap < #@]


NB.*ismap v being a map
NB. BOOL=: ismap MAP

ismap=: ((2: = {:) *. 2: = #)@$


NB.*getmap v [dyad] value for a key
NB. VALUE=: 'key' getmap MAP

NB.*getmap v [monad] list map keys
NB. KEYS=: geymap MAP

getmap=: ({."1) : ((,&1@ndxmap {:: ]) :: empty)


NB.*setmap a [dyad] add/change map value
NB. MAP=: VALUE 'key' setmap MAP

NB.*setmap a [monad] remove map entry
NB. MAP=: 'key' setmap MAP

setmap=: 1 : 0
  if. (#y.) <: i=. u. ndxmap y. do.
    y. return. end.
  (0 i } (#y.)#1) # y.
:
  if. (#y.) <: i=. u. ndxmap y. do.
    y.,(boxopen u.),<x. return. end.
  (< x.) (<i,1) } y.
)

NB.*DELIM v nested keys delimiter
NB. in 'k1.k2.k3' DELIM=: '.' (default)

DELIM=: '.'


NB.*getmapx v [dyad] value for a deep nexted key
NB. VALUE=: 'k1.k2.k3' getmapx MAPX

NB.*getmapx v [monad] list nested map keys
NB. KEYS=: geymapx MAP

getmapx=: 3 : 0
  r=. ''
  if. ismap y. do.
    for_i. getmap y. do.
      r=. r, i [`(  (, DELIM&,)&.>  )@.(*@#@]) getmapx i getmap y.
  end. end.
:
  for_i. <;._2 (>x.),DELIM do.
    y.=. i getmap y.
  end.
)

NB.*setmapx a [dyad] add/change value for a deep nexted key
NB. MAPX=: VALUE 'k1.k2.k3' setmapx MAPX

NB.*setmapx a [monad] remove a nested entry
NB. MAPX=: 'k1.k2.k3' setmapx MAPX

setmapx=: 1 : 0
  (empty'') u. setmapx y.
:
  E=. x. -: empty''
  r=. ,< y=. y.
  for_i. }:KEYS=. <;._2 (>u.),DELIM do.
    y=. i getmap y
    if. (#*.-.@ismap) y do.
      if. E do. y. return. end.
      y=. ,:'';y
    end.
    r=. r, <y
  end.
  if. E do.
    if. (<empty'') e. r do. y. return. end.
    q=. ({:KEYS) setmap >{:r
  else.
    q=. x.
  end.
  for_i. |.i.(#KEYS)-E do.
    q=. q (i{KEYS) setmap >i{r
  end.
)

NB.*flatmap v convert nested map to flat map of leaves
NB. MAP=: flatmap MAPX

flatmap=: ([ ,. (getmapx&.> <))~ getmapx

NB.*flatmapx v convert flat map to nested
NB. MAPX=: flatmapx MAP

flatmapx=: 3 : 0
  r=. empty''
  for_i. y. do.
    'k v'=. i
    r=. v k setmapx r
  end.
)

linear=: 3 : '5!:5<''y.'''
box2str=: ;@:(,&LF&.>)@(<"1@:>@{. <@(, ' '&,)&>"0 (linear&.>)@{: )@|:
str2box=: (i.&' ' ({. ; ".@}.) ]);._2

NB.*strmap v convert nested map to multiline string
NB. STR=: strmap MAPX

strmap=: box2str @: flatmap

NB.*strmapx v convert multiline string to nested map
NB. MAPX=: strmapx STR

strmapx=: flatmapx @: str2box

stemtails=: ;@:(<@(i.&' ' {. ]) (<@,  ,&LF)&>"0 ' .'&(E. <;._1 ]))

NB.*strmapc v convert compact string to nested map
NB. MAPX=: strmapc CSTR
NB.
NB. Compact string representation of a nested map is an extension
NB. of multiline string (strmap), except it allows multiple keys per line.
NB.
NB. Each line of compact string consists of
NB. stem .tail1 value1 .tail2 value2  ...
NB.   stem:   first non-space chars,
NB.           forms beggining of the key
NB.   tailI:  non-space chars preceded with ' .'
NB.           forms end of key
NB.   valueI: value of the key
NB.
NB. Both stem and tails may contain multiple parts of the key,
NB. separated with '.', but it's important that tails always
NB. begin with a '.'

strmapc=: [: strmapx [: ; <@stemtails;._2