derdaani
derdaani

Reputation: 588

Create keyed Maps from nested Lists with Immutable.js

I am working with a dataset that cannot be modified on the server side. So I am trying to setup the local data model on the client in a way that I can easily traverse through the model when updating parts of the data.

Therefore I am trying to create a multi-leveled Map from multi-leveled Maps including Lists, that themselves include Maps, etc. (see schematics at the end of this post).

What I am trying to get is a Map containing other Maps, with the key of the included Map being the value of the object (again please see schematics at the end of this post).

I got it to work on the first level:

const firstLevel = data.toMap().mapKeys((key, value) => value.get('value'));

See it in action here: https://jsfiddle.net/9f0djcb0/4/

But there is a maximum of 3 levels of nested data and I can't get my head around how to get the transformation done. Any help appreciated!

The schematic datasets:

// This is what I got
const dataset = [
  {
    field: 'lorem',
    value: 'ipsum',
    more: [
      {
        field: 'lorem_lvl1',
        value: 'ispum_lvl1',
        more: [
          {
            field: 'lorem_lvl2',
            value: 'ispum_lvl2',
            more: [
              {
                field: 'lorem_lvl3',
                value: 'ispum_lvl3',
              }
            ]
          }
        ]
        }
    ]
  },
  {
  field: 'glorem',
  value: 'blipsum'
  },
  {
  field: 'halorem',
  value: 'halipsum'
  }
];

This is where I want to go:

// This is what I want
const dataset_wanted = {
  ipsum: {
    field: 'lorem',
    value: 'ipsum',
    more: {
      lorem_lvl1: {
        field: 'lorem_lvl1',
        value: 'ispum_lvl1',
        more: {
          lorem_lvl2: {
            field: 'lorem_lvl2',
            value: 'ispum_lvl2',
            more: {
              lorem_lvl3: {
                field: 'lorem_lvl3',
                value: 'ispum_lvl3',
              }
            }
          }
        }
        }
    }
  },
  glorem: {
    field: 'glorem',
    value: 'blipsum'
  },
  halorem: {
    field: 'halorem',
    value: 'halipsum'
  }
};

Upvotes: 0

Views: 261

Answers (3)

derdaani
derdaani

Reputation: 588

So after some time passed I came up with a solution that works for me:

let sec, third, objThird;

// 1st level: simple mapping
const firstLevel = data.toMap().mapKeys((key, value) => value.get('value'));

// 2nd level: walk through updated firstLevel's subobjects and do the mapping again:
const secondLevel = firstLevel.map((obj) => {
  if (obj.has('more')) {
    sec = obj.get('more').toMap().mapKeys((key, value) => value.get('value'));

    // 3nd level: walk through updated secondLevel's subobjects and do the mapping again:
    objThird = sec.map((o) => {
      if (o.has('more')) {
        third = o.get('more').toMap().mapKeys((key, value) => value.get('value'));
        o = o.set('more', third);
      }
      return o;
    });
    obj = obj.set('more', objThird);
  }
  return obj;
});

See it in action here: https://jsfiddle.net/9f0djcb0/7/

This has been working nicely so far, thur pretty hard-coded. If anyone has a more elegant solution to this, I am happy to learn about it!

Upvotes: 0

derdaani
derdaani

Reputation: 588

As for a more generative solution, I re-wrote the answer before to a recursive approach:

function mapDeep(firstLevel) {
  return firstLevel.map((obj) => {
    if (obj.has('more')) {
      const sec = obj.get('more').toMap().mapKeys((key, value) => value.get('value'));
      const objNext = mapDeep(sec);
      obj = obj.set('more', objNext);
    }
    return obj;
  });
}

The first level still needs to be mapped manually before.

const firstLevel = data.toMap().mapKeys((key, value) => value.get('value'));
const secondLevel = mapDeep(firstLevel);

Again, see it in action: https://jsfiddle.net/9f0djcb0/12/

This is good enough for me for now. Still feels like this can be solved smarter (and more performant).. Cheers :)

Upvotes: 0

Coder
Coder

Reputation: 46

Retrieve nested structures using "getIn" is beter.

const data = Immutable.fromJS(dataset[0]);
const firstLevel = data.getIn(['more']);
const twoLevel = firstLevel.getIn([0,'more']);
const threeLevel = twoLevel.getIn([0,'more']);
console.log(firstLevel.toJS(),twoLevel.toJS(),threeLevel.toJS());

Upvotes: 0

Related Questions