首页 > 解决方案 > JavaScript - How to merge a complex nested object using another as reference to iterate?

问题描述

I have an object with multiple keys in the shape of:

{
    '.[key1].[key2].[key3]': {},
    '.[key1].[key2].[key3].[key4]': {},
    '.[key1].[key2].[key3].[key5]': {},
}

Given this example is pretty obvious that I generated this code using some reference in order to create these keys.

Below is the reference object

const invoiceShape = {
    'cfdi\\:CdfiRelacionados': {
      'tfd\\:CfdiRelacionado': {},
    },
    'cfdi\\:Emisor': {},
    'cfdi\\:Receptor': {},
    'cfdi\\:Conceptos': {
      'cfdi\\:Concepto': {
        'cfdi\\:Impuestos': {
          'cfdi\\:Traslados': {
            'cfdi\\:Traslado': {},
          },
          'cfdi\\:Retenciones': {
            'cfdi\\:Retencion': {},
          },
          'cfdi\\:InformacionAduanera': {},
          'cfdi\\:CuentaPredial': {},
          'cfdi\\:Parte': {
            'cfdi\\:InformacionAduanera': {},
          },
        },
      },
    },
    'cfdi\\:Complemento': {
      'tfd\\:TimbreFiscalDigital': {},
    },
    'cfdi\\:Addenda': {},
  };

So, my object looks like this

const invoiceParsed = {
    '.cfdi\\:Conceptos.cfdi\\:Concepto.cfdi\\:Impuestos': {},
    '.cfdi\\:Conceptos.cfdi\\:Concepto.cfdi\\:Impuestos.cfdi\\:Traslados': {},
    '.cfdi\\:Conceptos.cfdi\\:Concepto.cfdi\\:Impuestos.cfdi\\:Retenciones': {},
}

The invoiceShape object has empty values on the keys, but now that the invoiceParsed object is filled I want it to fill the invoiceShape as well, I figured out that parsing every nested level spliting the "." in the keys of invoiceParsed is a more effective way to iterate over the reference object, but if I iterate over the 'cfdi\\:Conceptos' key a second time I just overwrite the key value, instead of merging it

I'm not posting any code on the iteration, because I want read your ideas, algorithms or packages you would use as lodash for example

标签: javascriptnode.jsecmascript-6

解决方案


尽管partial.lenses是处理深度嵌套数据的另一个出色工具,但我喜欢Ramda的类似功能。

这里有两个可行的解决方案。第一个使用 Ramda 的几个函数。第二个主要是 vanilla JS,只有 Ramda 的两个功能:

const shape = {
  a: {
    b: {
      c: {}
    }
  },
  d: {
    e: {
      f: {
        g: {}
      }
    }
  },
  h: {}
}

const parsed = {
  'a': { v: 1 },
  'a.b': { v: 2 },
  'a.b.c': { v: 3 },
  'd': { v: 4 },
  'd.e': { v: 5 },
  'd.e.f': { v: 6 },
  'd.e.f.g': { v: 7 },
  'h': { v: 8 }
}

const fillShapeWithParsed = s =>
  R.pipe(
    R.toPairs,
    R.reduce(
      (acc, [k, v]) =>
        R.over(R.lensPath(k.split('.')), R.merge(v), acc),
        s
    )
  )

console.log(
  fillShapeWithParsed(shape)(parsed)
)

const merge = x => y => ({ ...y, ...x })

const fillShapeWithParsedVanilla = s => p =>
  Object.entries(p).reduce(
    (acc, [k, v]) =>
      R.over(R.lensPath(k.split('.')), merge(v), acc),
      s
  )

console.log(
  fillShapeWithParsedVanilla(shape)(parsed)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

对于同时使用 Ramda 和 partial.lenses 的混合解决方案,只需换掉示例中R.over(以此开头的行:

L.modify(k.split('.'), R.merge(v), acc)

推荐阅读