Search code examples
immutable.jsnormalizr

How to alter keys in immutable map?


I've a data structure like this (generated by normalizr):

const data = fromJS({
  templates: {
    "83E51B08-5F55-4FA2-A2A0-99744AE7AAD3":
      {"uuid": "83E51B08-5F55-4FA2-A2A0-99744AE7AAD3", test: "bla"},
    "F16FB07B-EF7C-440C-9C21-F331FCA93439":
      {"uuid": "F16FB07B-EF7C-440C-9C21-F331FCA93439", test: "bla"}
  }
})

Now I try to figure out how to replace the UUIDs in both the key and the value of the template entries. Basically how can I archive the following output:

const data = fromJS({
  templates: {
    "DBB0B4B0-565A-4066-88D3-3284803E0FD2":
      {"uuid": "DBB0B4B0-565A-4066-88D3-3284803E0FD2", test: "bla"},
    "D44FA349-048E-4006-A545-DBF49B1FA5AF":
      {"uuid": "D44FA349-048E-4006-A545-DBF49B1FA5AF", test: "bla"}
  }
})

A good candidate seems to me the .mapEntries() method, but I'm struggling on how to use it ...

// this don't work ... :-(
const result = data.mapEntries((k, v) => {
  const newUUID = uuid.v4()
  return (newUUID, v.set('uuid', newUUID))
})

Maybe someone can give me a hand here?


Solution

  • mapEntries is the correct method. From the documentation, the mapping function has the following signature:

    mapper: (entry: [K, V], index: number, iter: this) => [KM, VM]
    

    This means that the first argument is the entry passed in as an array of [key, value]. Similarly, the return value of the mapper function should be an array of the new key and the new value. So your mapper function needs to look like this:

    ([k, v]) => {
      const newUUID = uuid.v4()
      return [newUUID, v.set('uuid', newUUID)]
    } 
    

    This is equivalent to the following (more explicit) function:

    (entry) => {
      const key = entry[0]; // note that key isn't actually used, so this isn't necessary
      const value = entry[1];
      const newUUID = uuid.v4()
      return [newUUID, value.set('uuid', newUUID)]
    }
    

    One thing to note is that the templates are nested under the templates property, so you can't map data directly -- instead you'll want to use the update function.

    data.update('templates', templates => template.mapEntries(...)))
    

    So putting everything together, your solution should look like the following:

    const result = data.update('templates', templates => 
      templates.mapEntries(([k, v]) => {
        const newUUID = uuid.v4()
        return [newUUID, v.set('uuid', newUUID)]
      })
    );