hughsk / flat

:steam_locomotive: Flatten/unflatten nested Javascript objects

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Possible to allow key's with '.'s in them?

sorahn opened this issue · comments

I'm looking for a way to preserve keys that have .s in to use the same syntax as lodash's set/get.

Ideally something like this:

// Input
{
  some: {
    'key.with.dots': value
  }
}

// Output
{
  some['key.with.dots']: value
}

The problem I've found with using the transformKey option is they still get the . joining them.

I have this:

const flatData = flatten(data, {
  transformKey: key => (key.includes('.') ? `['${key}']` : key),
});

But the output looks like this. (extra . after some)

{
  some.['key.with.dots']: value
}

I've worked around the issue for now by looping through the flattend data and doing a string replace on .[ with [.

const fixedData = Object.entries(flatData).reduce((acc, [key, value]) => {
  const newKey = key.includes('.[') ? key.replace('.[', '[') : key;
  acc[newKey] = value;
  return acc;
}, {});

@sorahn I think a feature would need to be added. But if you can use this package to unflatten as well, then you could just use a different delimiter like /. In which case, without transformKey, you would get:

{
  "some/key.with.dots": value
}

Using the same delimiter when unflattening you would get your original structure.

If you have / in your keys, try using transformKey with something like encodeURIComponent. (decodeURIComponent when unflattening)

@rgraffbrd Hmm, that might work. I'd have to think through it. We feed the flattened data into formControlNames (angular), then use lodash for updates (set/omit fields) and comparisons to the original data (get).

@sorahn Not up on my Angular, but are you essentially saying that you set a form control's name to the full path and then, using lodash, you patch the original data/object structure from a flattened object with the changes?

Yes that's pretty close. We flatten the object. and use the path as the form name for angular to manage. so roughly <input name="foo['bar.baz']">, Then we listen to all the form value changes where you get a callback that looks something like this.

(path, value) => {
  if (value === null) {
    return omit(path, value, data)
  }

  return set(path, value, data)
}

elsewhere, we are also using get to compare to the original value to mark a field as edited. So when you pass the path into our component it both sets it on the <input> and uses it to check to see if the field was edited.