通过匹配ID更新嵌套对象

时间:2020-04-30 22:46:41

标签: javascript arrays object data-structures

我有一个带有嵌套对象的数组,如果它们匹配,我需要从另一个对象数组更新它们。

这是我要更新的数据结构:

const invoices = {
 BatchItemRequest: [
 {
  bId: "bid10",
  Invoice: {
    Line: [
      {
        SalesItemLineDetail: {
          ItemAccountRef: { AccountCode: "10110" },
        },
      },
      {
        SalesItemLineDetail: {
          ItemAccountRef: { AccountCode: "11110" },
        },
        Amount: 2499,
      },
    ],
  },
},
{
  bId: "bid10",
  Invoice: {
    Line: [
      {
        SalesItemLineDetail: {
          ItemAccountRef: { AccountCode: "10110" },
        },
      },
      {
        SalesItemLineDetail: {
          ItemAccountRef: { AccountCode: "10111" },
        },
        Amount: 2499,
      },
    ],
  },
},
],
};

这是我要从中更新对象的数组:

const accounts = [
 { AccountCode: "10110", Id: "84" },
 { AccountCode: "11110", Id: "5" },
 { AccountCode: "10111", Id: "81" },
];

我想使用帐户更新发票,如果AccountCode匹配,则插入ID,以获得以下结构:

const invoices = {
 BatchItemRequest: [
 {
  bId: "bid10",
  Invoice: {
    Line: [
      {
        SalesItemLineDetail: {
          ItemAccountRef: { AccountCode: "10110", Id: "84" },
        },
      },
      {
        SalesItemLineDetail: {
          ItemAccountRef: { AccountCode: "11110", Id: "5" },
        },
        Amount: 2499,
      },
    ],
  },
},
{
  bId: "bid10",
  Invoice: {
    Line: [
      {
        SalesItemLineDetail: {
          ItemAccountRef: { AccountCode: "10110", Id: "84" },
        },
      },
      {
        SalesItemLineDetail: {
          ItemAccountRef: { AccountCode: "10111", Id: "81" },
        },
        Amount: 2499,
      },
    ],
  },
},
],
};

我尝试了各种方法,例如:

const mapped = invoices.BatchItemRequest.map((item1) => {
return Object.assign(
  item1,
  accounts.find((item2) => {
    return item2 && item1.Invoice.Line.ItemAccountRef.AccountCode === item2.AccountCode;
  })
);
});

这种方法存在问题(它不起作用,因为我认为我需要做另一个嵌套地图),但它还会创建一个新数组,仅包含发票的嵌套元素。

有人知道这种方法很好吗?

2 个答案:

答案 0 :(得分:1)

这不是最干净的代码,但是可以完成工作:

function matchInvoiceWithAccount(invoices, accounts) {
    const mappedInvoices = invoices.BatchItemRequest.map((request) => {
        // Shouldn't modify input parameter, could use Object.assign to create a copy and modify the copy instead for purity
        request.Invoice.Line = request.Invoice.Line.map((line) => {
            const accountCode = line.SalesItemLineDetail.ItemAccountRef.AccountCode;
            // If accounts was a map of AccountCode to Id you would't need to search for it which would be more effective
            const account = accounts.find((account) => account.AccountCode === accountCode);

            if (account) {
                line.SalesItemLineDetail.ItemAccountRef.Id = account.Id;
            }

            return line;
        });

        return request;
    });

    return {
        BatchItemRequest: mappedInvoices,
    };
}

您可能并且可能应该做的是改进此操作,而不修改函数的输入参数,但这要求您以更好的方式使用Object.assign或spread运算符复制原始参数。

答案 1 :(得分:1)

首先,最好从您的accounts数组创建Map。我们将一次使用O(n)进行数组处理,然后使用O(1)的代码读取ID。嵌套的fors是O(m * n),这在大数组时会慢得多。

const idsByAccountCodes = new Map();
accounts.forEach((data) => {
    idsByAccountCodes.set(data.AccountCode, data.Id);
})

或更短:

const idsByAccountCode = new Map(accounts.map((data) => [data.AccountCode, data.Id]))

然后,如果您要更改原始值,则可以遍历所有嵌套级别并添加值

for ( const {Invoice:{ Line: line }} of invoices.BatchItemRequest){
    for ( const {SalesItemLineDetail: {ItemAccountRef: item}} of line){
      item.Id = idsByAccountCodes.get(item.AccountCode) || 'some default value'
      // also if you don't have ids for all codes you need to define logic for  that case
    }
}

如果您不需要变异原始的大对象“发票”和所有嵌套对象,则可以使用lodash.cloneDeep之类的东西创建if的递归克隆