Javascript:我正在更新对象键的值。但是更新后另一个密钥对也改变了价值
Javascript: I am updating the value of an object key. BUT after the update another key pair also changes value
我正在为发票程序开发 API。我的问题是,当我尝试构造一个 json 并更新一些键时,一个键在我没有更新的情况下发生了变化。
这是一个没有所有 api 调用的 MRE。我只是将准备好的对象用于数据。
const business = {
vatNumber: '050112718',
country: 'GR',
branch: 0
}
const customer = {
vatNumber: '050112915',
country: 'GR',
branch: 0,
postalCode: 28100,
city: 'Argostoli'
}
const invoiceProperties = {
series: 0,
aa: 1,
issueDate: '2021-12-15',
type: '1_1',
currency: 'EUR'
}
const payment = {
type: 3
}
const product = {
price: 10,
taxCode: 1,
classificationType: 'type a',
classificationCategory: 'category 1',
}
const lines = [
{
lineNumber: 1,
netValue: 0.00,
vatCategory: 0,
vatAmount: 0,
incomeClassification: {
'icls:classificationType': '',
'icls:classificationCategory': '',
'icls:amount': 0.00
}
},
{
lineNumber: 2,
netValue: 0.00,
vatCategory: 0,
vatAmount: 0,
incomeClassification: {
'icls:classificationType': '',
'icls:classificationCategory': '',
'icls:amount': 0.00
}
},
{
lineNumber: 3,
netValue: 0.00,
vatCategory: 0,
vatAmount: 0,
incomeClassification: {
'icls:classificationType': '',
'icls:classificationCategory': '',
'icls:amount': 0.00
}
}
]
// json invoice creation
const invoiceObj = {
invoice: {
issuer: {
vatNumber: business.vatNumber,
country: business.country,
branch: business.branch
},
counterpart: {
vatNumber: customer.vatNumber,
country: customer.country,
branch: customer.branch,
address: {
postalCode: customer.postalCode,
city: customer.city
}
},
invoiceHeader: {
series: invoiceProperties.series,
aa: invoiceProperties.aa,
issueDate: invoiceProperties.issueDate,
invoiceType: invoiceProperties.type,
currency: invoiceProperties.currency
},
paymentMethods: {
paymentMethodDetails: {
type: payment.type,
amount: ""
}
},
invoiceDetails: [],
invoiceSummary: {
totalNetValue: 0,
totalVatAmount: 0,
totalWithheldAmount: '0.00',
totalFeμesAmount: '0.00',
totalStampDutyAmount: '0.00',
totalOtherTaxesAmount: '0.00',
totalDeductionsAmount: '0.00',
totalGrossValue: 0,
incomeClassification: []
}
}
}
//making paths into object more simle
const invoiceDetails = invoiceObj.invoice.invoiceDetails;
const summaryClassification = invoiceObj.invoice.invoiceSummary.incomeClassification;
const makeLines = ()=> {
for (const line of lines) {
line.netValue = product.price.toFixed(2);
line.vatCategory = product.taxCode;
line.incomeClassification["icls:classificationType"] = product.classificationType;
line.incomeClassification["icls:classificationCategory"] = product.classificationCategory;
line.incomeClassification["icls:amount"] = product.price.toFixed(2);
switch (line.vatCategory) {
case 1:
line.vatAmount = (line.netValue * 0.24).toFixed(2);
break;
case 2:
line.vatAmount = (line.netValue * 0.17).toFixed(2);
break;
case 3:
line.vatAmount = (line.netValue * 0.13).toFixed(2);
break;
case 4:
line.vatAmount = (line.netValue * 0.09).toFixed(2);
break;
case 5:
line.vatAmount = (line.netValue * 0.06).toFixed(2);
break;
case 6:
line.vatAmount = (line.netValue * 0.04).toFixed(2);
break;
case 7:
line.vatAmount = '0.00';
break;
case 8:
line.vatAmount = '0.00';
break;
};
console.log(line); //just to see something in dev mode
invoiceObj.invoice.invoiceSummary.totalNetValue = (invoiceObj.invoice.invoiceSummary.totalNetValue/1 + line.netValue/1).toFixed(2);
invoiceObj.invoice.invoiceSummary.totalVatAmount = (invoiceObj.invoice.invoiceSummary.totalVatAmount/1 + line.vatAmount/1).toFixed(2);
invoiceObj.invoice.invoiceSummary.totalGrossValue = (invoiceObj.invoice.invoiceSummary.totalGrossValue/1 + line.netValue/1 + line.vatAmount/1).toFixed(2);
const lineClassification = line.incomeClassification;
const lineType = lineClassification["icls:classificationType"];
const lineCategory = lineClassification["icls:classificationCategory"];
const classificationIndex = summaryClassification.findIndex(o => o["icls:classificationType"] === lineType && o["icls:classificationCategory"] === lineCategory);
// if there isn't a array item with the same combination of categories, we add the classifications object
if (classificationIndex < 0) {
summaryClassification.push(lineClassification);
} else { // else if there is a an item in the array with the same categories, we add the amount of the item to the total
summaryClassification[classificationIndex]["icls:amount"] = (summaryClassification[classificationIndex]["icls:amount"]/1 + line.netValue/1 ).toFixed(2);
}
invoiceDetails.push(line);
}
}
- 我们有 3 个线对象到“线”数组。
- 我遍历数组以填充每一行的信息。
- 每一行都有自己的收入分类
- 有一个汇总对象需要收集每个分类组合。 (在示例中我只使用了一种组合)
- 迭代后,我正在填写需要填写的汇总字段(totalNetValue、totalVatAmount、totalGrossValue)。
- 然后我使用 findIndex() 查找摘要中是否存在分类组合(类型和类别)。
- 如果 findIndex 小于 0(没有匹配该行的组合)我将添加该行的组合。
- 否则,我只是通过添加当前行来更新给定索引的数量。
我的问题
else 语句以某种方式改变了值 os 第一行的 incomeClassification。 +
鉴于从第一次迭代开始,该键应该是“10.00”,紧随代码 line.incomeClassification["icls:amount"] = product.price.toFixed(2);
和 product.price beeing = 10。
此外,显然 else 语句代码更新了摘要属性而不是行属性 summaryClassification[classificationIndex]["icls:amount"] = (summaryClassification[classificationIndex]["icls:amount"]/1 + line.netValue/1 ).toFixed(2);
。
如果我在 makeLines() 的和处注释掉 else 语句:
- 第一行分类金额给我的预期价格是“10.00”,但是
- 汇总分类金额卡在 10.00,因为它没有
获取每个新行的更新。
也许还有另一种方法可以做到这一点,但为什么首先会发生这种情况?
任何帮助将不胜感激...
解决眼前的问题:每个 line
都有一个 lineClassification
道具,它本身就是一个对象。随着 lines
被迭代,其中一些 lineClassification 被放置在另一个数组 (summaryClassification
) 中。外循环的后续轮次是查找数组的元素并改变它们,但这些 lineClassifications 是 lines.
指向的相同对象
这里有一个更简单的 MRE 来说明...
const lines = [
{ name: 'line0', classificationObject: { name: 'class0', number: 10 } },
{ name: 'line1', classificationObject: { name: 'class1', number: 10 } },
{ name: 'line2', classificationObject: { name: 'class0', number: 10 } },
]
let summary = []
for (line of lines) {
let classificationObject = line.classificationObject;
let classificationName = classificationObject.name
let index = summary.findIndex(s => s.name === classificationName);
if (index == -1) {
summary.push(classificationObject); // the problem is here.
// the SAME classification object now appears in TWO places:
// in the line object, and in the summary array
// future mutations will be seen by both
} else {
// as a result, this line seems to modify two objects
// it's really modifying one object, pointed to from multiple places
summary[index].number += 12
}
}
console.log("logging the summary, it's what we expect. The saved class0 classification has been mutated:")
console.log(summary)
console.log("But logging the lines, we're surprised to find line 0's classification object has been mutated. it happens because that same object was also placed in the summary array, and was mutated in a subequent turn of the loop:")
console.log(lines)
修复 MRE:
显然 summaryClassification
数组必须被赋予 lineClassification
对象的副本,一个可以独立于 line
它曾经属于。
const lines = [
{ name: 'line0', classificationObject: { name: 'class0', number: 10 } },
{ name: 'line1', classificationObject: { name: 'class1', number: 10 } },
{ name: 'line2', classificationObject: { name: 'class0', number: 10 } },
]
let summary = []
for (line of lines) {
// do some stuff that doesn't matter
let classificationObject = line.classificationObject;
let classificationName = classificationObject.name
let index = summary.findIndex(s => s.name === classificationName);
if (index == -1) {
let classificationCopy = Object.assign({}, classificationObject); // see here: make a copy
summary.push(classificationCopy); // subsequent turns will
// mutate the copy, leaving the original line alone
} else {
summary[index].number += 12
}
}
console.log(summary)
console.log("logging the lines, see what we expect in line 0, an unchanged classification object")
console.log(lines)
OP 术语:
if (classificationIndex < 0) {
let lineClassificationCopy = Object.assign({}, lineClassification); // see here: make a copy
summaryClassification.push(lineClassificationCopy);
} else { // else if there is a an item in the array with the same categories, we add the amount of the item to the total
summaryClassification[classificationIndex]["icls:amount"] = (summaryClassification[classificationIndex]["icls:amount"]/1 + line.netValue/1 ).toFixed(2);
}
顺便说一句,代码将数值表示为字符串。更好的做法是将数字表示为数字,对数字进行数学运算,将中间结果存储为数字等,然后将数字转换为字符串 仅当 必须将它们呈现给用户时。对于货币,这应该使用区域设置敏感格式来完成。
此外,该代码似乎旨在使用不够强大的技术进行现实生活中的复杂业务计算。如果这是一个生产系统,我担心最终的所有者会发现它不透明且无法维护。我建议在下一次修订中审查和使用 object oriented concepts。
我正在为发票程序开发 API。我的问题是,当我尝试构造一个 json 并更新一些键时,一个键在我没有更新的情况下发生了变化。
这是一个没有所有 api 调用的 MRE。我只是将准备好的对象用于数据。
const business = {
vatNumber: '050112718',
country: 'GR',
branch: 0
}
const customer = {
vatNumber: '050112915',
country: 'GR',
branch: 0,
postalCode: 28100,
city: 'Argostoli'
}
const invoiceProperties = {
series: 0,
aa: 1,
issueDate: '2021-12-15',
type: '1_1',
currency: 'EUR'
}
const payment = {
type: 3
}
const product = {
price: 10,
taxCode: 1,
classificationType: 'type a',
classificationCategory: 'category 1',
}
const lines = [
{
lineNumber: 1,
netValue: 0.00,
vatCategory: 0,
vatAmount: 0,
incomeClassification: {
'icls:classificationType': '',
'icls:classificationCategory': '',
'icls:amount': 0.00
}
},
{
lineNumber: 2,
netValue: 0.00,
vatCategory: 0,
vatAmount: 0,
incomeClassification: {
'icls:classificationType': '',
'icls:classificationCategory': '',
'icls:amount': 0.00
}
},
{
lineNumber: 3,
netValue: 0.00,
vatCategory: 0,
vatAmount: 0,
incomeClassification: {
'icls:classificationType': '',
'icls:classificationCategory': '',
'icls:amount': 0.00
}
}
]
// json invoice creation
const invoiceObj = {
invoice: {
issuer: {
vatNumber: business.vatNumber,
country: business.country,
branch: business.branch
},
counterpart: {
vatNumber: customer.vatNumber,
country: customer.country,
branch: customer.branch,
address: {
postalCode: customer.postalCode,
city: customer.city
}
},
invoiceHeader: {
series: invoiceProperties.series,
aa: invoiceProperties.aa,
issueDate: invoiceProperties.issueDate,
invoiceType: invoiceProperties.type,
currency: invoiceProperties.currency
},
paymentMethods: {
paymentMethodDetails: {
type: payment.type,
amount: ""
}
},
invoiceDetails: [],
invoiceSummary: {
totalNetValue: 0,
totalVatAmount: 0,
totalWithheldAmount: '0.00',
totalFeμesAmount: '0.00',
totalStampDutyAmount: '0.00',
totalOtherTaxesAmount: '0.00',
totalDeductionsAmount: '0.00',
totalGrossValue: 0,
incomeClassification: []
}
}
}
//making paths into object more simle
const invoiceDetails = invoiceObj.invoice.invoiceDetails;
const summaryClassification = invoiceObj.invoice.invoiceSummary.incomeClassification;
const makeLines = ()=> {
for (const line of lines) {
line.netValue = product.price.toFixed(2);
line.vatCategory = product.taxCode;
line.incomeClassification["icls:classificationType"] = product.classificationType;
line.incomeClassification["icls:classificationCategory"] = product.classificationCategory;
line.incomeClassification["icls:amount"] = product.price.toFixed(2);
switch (line.vatCategory) {
case 1:
line.vatAmount = (line.netValue * 0.24).toFixed(2);
break;
case 2:
line.vatAmount = (line.netValue * 0.17).toFixed(2);
break;
case 3:
line.vatAmount = (line.netValue * 0.13).toFixed(2);
break;
case 4:
line.vatAmount = (line.netValue * 0.09).toFixed(2);
break;
case 5:
line.vatAmount = (line.netValue * 0.06).toFixed(2);
break;
case 6:
line.vatAmount = (line.netValue * 0.04).toFixed(2);
break;
case 7:
line.vatAmount = '0.00';
break;
case 8:
line.vatAmount = '0.00';
break;
};
console.log(line); //just to see something in dev mode
invoiceObj.invoice.invoiceSummary.totalNetValue = (invoiceObj.invoice.invoiceSummary.totalNetValue/1 + line.netValue/1).toFixed(2);
invoiceObj.invoice.invoiceSummary.totalVatAmount = (invoiceObj.invoice.invoiceSummary.totalVatAmount/1 + line.vatAmount/1).toFixed(2);
invoiceObj.invoice.invoiceSummary.totalGrossValue = (invoiceObj.invoice.invoiceSummary.totalGrossValue/1 + line.netValue/1 + line.vatAmount/1).toFixed(2);
const lineClassification = line.incomeClassification;
const lineType = lineClassification["icls:classificationType"];
const lineCategory = lineClassification["icls:classificationCategory"];
const classificationIndex = summaryClassification.findIndex(o => o["icls:classificationType"] === lineType && o["icls:classificationCategory"] === lineCategory);
// if there isn't a array item with the same combination of categories, we add the classifications object
if (classificationIndex < 0) {
summaryClassification.push(lineClassification);
} else { // else if there is a an item in the array with the same categories, we add the amount of the item to the total
summaryClassification[classificationIndex]["icls:amount"] = (summaryClassification[classificationIndex]["icls:amount"]/1 + line.netValue/1 ).toFixed(2);
}
invoiceDetails.push(line);
}
}
- 我们有 3 个线对象到“线”数组。
- 我遍历数组以填充每一行的信息。
- 每一行都有自己的收入分类
- 有一个汇总对象需要收集每个分类组合。 (在示例中我只使用了一种组合)
- 迭代后,我正在填写需要填写的汇总字段(totalNetValue、totalVatAmount、totalGrossValue)。
- 然后我使用 findIndex() 查找摘要中是否存在分类组合(类型和类别)。
- 如果 findIndex 小于 0(没有匹配该行的组合)我将添加该行的组合。
- 否则,我只是通过添加当前行来更新给定索引的数量。
我的问题
else 语句以某种方式改变了值 os 第一行的 incomeClassification。 +
鉴于从第一次迭代开始,该键应该是“10.00”,紧随代码 line.incomeClassification["icls:amount"] = product.price.toFixed(2);
和 product.price beeing = 10。
此外,显然 else 语句代码更新了摘要属性而不是行属性 summaryClassification[classificationIndex]["icls:amount"] = (summaryClassification[classificationIndex]["icls:amount"]/1 + line.netValue/1 ).toFixed(2);
。
如果我在 makeLines() 的和处注释掉 else 语句:
- 第一行分类金额给我的预期价格是“10.00”,但是
- 汇总分类金额卡在 10.00,因为它没有 获取每个新行的更新。
也许还有另一种方法可以做到这一点,但为什么首先会发生这种情况?
任何帮助将不胜感激...
解决眼前的问题:每个 line
都有一个 lineClassification
道具,它本身就是一个对象。随着 lines
被迭代,其中一些 lineClassification 被放置在另一个数组 (summaryClassification
) 中。外循环的后续轮次是查找数组的元素并改变它们,但这些 lineClassifications 是 lines.
这里有一个更简单的 MRE 来说明...
const lines = [
{ name: 'line0', classificationObject: { name: 'class0', number: 10 } },
{ name: 'line1', classificationObject: { name: 'class1', number: 10 } },
{ name: 'line2', classificationObject: { name: 'class0', number: 10 } },
]
let summary = []
for (line of lines) {
let classificationObject = line.classificationObject;
let classificationName = classificationObject.name
let index = summary.findIndex(s => s.name === classificationName);
if (index == -1) {
summary.push(classificationObject); // the problem is here.
// the SAME classification object now appears in TWO places:
// in the line object, and in the summary array
// future mutations will be seen by both
} else {
// as a result, this line seems to modify two objects
// it's really modifying one object, pointed to from multiple places
summary[index].number += 12
}
}
console.log("logging the summary, it's what we expect. The saved class0 classification has been mutated:")
console.log(summary)
console.log("But logging the lines, we're surprised to find line 0's classification object has been mutated. it happens because that same object was also placed in the summary array, and was mutated in a subequent turn of the loop:")
console.log(lines)
修复 MRE:
显然 summaryClassification
数组必须被赋予 lineClassification
对象的副本,一个可以独立于 line
它曾经属于。
const lines = [
{ name: 'line0', classificationObject: { name: 'class0', number: 10 } },
{ name: 'line1', classificationObject: { name: 'class1', number: 10 } },
{ name: 'line2', classificationObject: { name: 'class0', number: 10 } },
]
let summary = []
for (line of lines) {
// do some stuff that doesn't matter
let classificationObject = line.classificationObject;
let classificationName = classificationObject.name
let index = summary.findIndex(s => s.name === classificationName);
if (index == -1) {
let classificationCopy = Object.assign({}, classificationObject); // see here: make a copy
summary.push(classificationCopy); // subsequent turns will
// mutate the copy, leaving the original line alone
} else {
summary[index].number += 12
}
}
console.log(summary)
console.log("logging the lines, see what we expect in line 0, an unchanged classification object")
console.log(lines)
OP 术语:
if (classificationIndex < 0) {
let lineClassificationCopy = Object.assign({}, lineClassification); // see here: make a copy
summaryClassification.push(lineClassificationCopy);
} else { // else if there is a an item in the array with the same categories, we add the amount of the item to the total
summaryClassification[classificationIndex]["icls:amount"] = (summaryClassification[classificationIndex]["icls:amount"]/1 + line.netValue/1 ).toFixed(2);
}
顺便说一句,代码将数值表示为字符串。更好的做法是将数字表示为数字,对数字进行数学运算,将中间结果存储为数字等,然后将数字转换为字符串 仅当 必须将它们呈现给用户时。对于货币,这应该使用区域设置敏感格式来完成。
此外,该代码似乎旨在使用不够强大的技术进行现实生活中的复杂业务计算。如果这是一个生产系统,我担心最终的所有者会发现它不透明且无法维护。我建议在下一次修订中审查和使用 object oriented concepts。