最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to prevent data loss when updating nested objects in Node.js? - Stack Overflow

programmeradmin0浏览0评论

I have an object called productionSheet which contains details of a plastic bag. Dimensions and other information are stored inside productionSheet.bagDetails. When my app's backend receives the productionSheet object from the frontend, the data must be passed to a function that calculates the bag's weight and adds the result to the object before saving it in the database. However, this operation overwrites other data within the object itself. Let me explain:

Backend router

productionSheetsRouter.post("/", checkJwt, async (req, res) => {
  try {
    let newProductionSheet = new ProductionSheet(req.body);
    newProductionSheet = calculateWeightSingleBag(newProductionSheet);

    await newProductionSheet.save();
    res.status(201).json(newProductionSheet);
  } catch (error) {
    console.error("Error in productionSheetsRouter.post:", error);
    res.status(400).json({ message: "Bad Request" });
  }
});

This is the calculateWeightSingleBag() function:

const calculateWeightSingleBag = (productionSheet) => {
  const widthDevelopment =
    Number(productionSheet.bagDetails.input.dimensions.width.value) +
    Number(productionSheet.bagDetails.input.gussets.openSideGusset.value);

  const heightDevelopment =
    Number(productionSheet.bagDetails.input.dimensions.height.value) +
    Number(productionSheet.bagDetails.input.flaps.upperFlapOpen.value) +
    Number(productionSheet.bagDetails.input.flaps.zipFlap.value) +
    Number(productionSheet.bagDetails.input.gussets.openBottomGusset.value) / 2;

  const heightDevelopmentNet =
    Number(productionSheet.bagDetails.input.dimensions.height.value) +
    Number(productionSheet.bagDetails.input.flaps.upperFlapOpen.value) / 2 +
    Number(productionSheet.bagDetails.input.flaps.zipFlap.value) +
    Number(productionSheet.bagDetails.input.gussets.openBottomGusset.value) / 2;

  const bareWeight = (
    widthDevelopment *
    heightDevelopment *
    2 *
    (Number(productionSheet.bagDetails.output.thickness.value) / 10000) *
    Number(productionSheet.bagDetails.output.specificWeight.value)
  ).toFixed(2);

  const netDieCutWeight = (
    widthDevelopment *
    heightDevelopmentNet *
    2 *
    (Number(productionSheet.bagDetails.output.thickness.value) / 10000) *
    Number(productionSheet.bagDetails.output.specificWeight.value) *
    (1 - Number(productionSheet.bagDetails.input.cutting.cuttingDie.value) / 100)
  ).toFixed(2);
  

  console.log("Before bagweight: ", productionSheet.bagWeight);
  productionSheet.bagWeight = {
    input: {
      ...productionSheet.bagWeight.input,
    },
    output: {
      ...productionSheet.bagWeight.output, 
      bareWeight: { value: bareWeight, cost: false, unit: "g" },
      netDieCutWeight: { value: netDieCutWeight, cost: false, unit: "g" },
    },
  };
  console.log("After bagweight: ", productionSheet.bagWeight);

  return productionSheet; 
};

export default calculateWeightSingleBag;

I have this result from the console.log():

Before bagweight:  {
  input: { netDieCutWeight: { value: 0, cost: false, unit: 'g' } },
  output: {
    bareWeight: { value: 0, cost: false, unit: 'g' },
    netDieCutWeight: { value: 0, cost: false, unit: 'g' },
    netDieCutWeightWithAccessories: { value: 0, cost: false, unit: 'g' }
  }
}
After bagweight:  {
  output: {
    bareWeight: { value: 1.56, cost: false, unit: 'g' },
    netDieCutWeight: { value: 1.47, cost: false, unit: 'g' }
  }
}

After bagweight should be:

  input: { netDieCutWeight: { value: 0, cost: false, unit: 'g' } },
  output: {
    bareWeight: { value: 1.56, cost: false, unit: 'g' },
    netDieCutWeight: { value: 1.47, cost: false, unit: 'g' }
    netDieCutWeightWithAccessories: { value: 0, cost: false, unit: 'g' }
  }
}

Req.Body is:

const emptyProductionSheetBackEnd = {
  userId: "",

  information: {
    input: {
      customer: "",
      itemCode: "",
      description: "",
    },
  },

  bagDetails: {
    input: {
      bagType: { value: "Zip bag", cost: false },
      dimensions: {
        width: { value: 10, cost: false, unit: "mm" },
        height: { value: 15, cost: false, unit: "mm" },
      },
      gussets: {
        openBottomGusset: { value: 0, cost: false, unit: "mm" },
        openSideGusset: { value: 0, cost: false, unit: "mm" },
      },
      flaps: {
        zipFlap: { value: 0, cost: false, unit: "mm" },
        upperFlapOpen: { value: 2, cost: false, unit: "mm" },
      },
      cutting: {
        cuttingDie: { value: 0, cost: false, unit: "mm" },
      },
    },
    output: {
      thickness: {
        value: 50,
        cost: false,
        unit: "microns",
      },
      specificWeight: {
        value: 0.92,
        cost: false,
        unit: "g/cm",
      },
    },
  },

  bagWeight: {
    input: {
      netDieCutWeight: { value: 0, cost: false, unit: "g" },
    },
    output: {
      bareWeight: { value: 0, cost: false, unit: "g" },
      netDieCutWeight: { value: 0, cost: false, unit: "g" },
      netDieCutWeightWithAccessories: { value: 0, cost: false, unit: "g" },
    },
  },

};

I have an object called productionSheet which contains details of a plastic bag. Dimensions and other information are stored inside productionSheet.bagDetails. When my app's backend receives the productionSheet object from the frontend, the data must be passed to a function that calculates the bag's weight and adds the result to the object before saving it in the database. However, this operation overwrites other data within the object itself. Let me explain:

Backend router

productionSheetsRouter.post("/", checkJwt, async (req, res) => {
  try {
    let newProductionSheet = new ProductionSheet(req.body);
    newProductionSheet = calculateWeightSingleBag(newProductionSheet);

    await newProductionSheet.save();
    res.status(201).json(newProductionSheet);
  } catch (error) {
    console.error("Error in productionSheetsRouter.post:", error);
    res.status(400).json({ message: "Bad Request" });
  }
});

This is the calculateWeightSingleBag() function:

const calculateWeightSingleBag = (productionSheet) => {
  const widthDevelopment =
    Number(productionSheet.bagDetails.input.dimensions.width.value) +
    Number(productionSheet.bagDetails.input.gussets.openSideGusset.value);

  const heightDevelopment =
    Number(productionSheet.bagDetails.input.dimensions.height.value) +
    Number(productionSheet.bagDetails.input.flaps.upperFlapOpen.value) +
    Number(productionSheet.bagDetails.input.flaps.zipFlap.value) +
    Number(productionSheet.bagDetails.input.gussets.openBottomGusset.value) / 2;

  const heightDevelopmentNet =
    Number(productionSheet.bagDetails.input.dimensions.height.value) +
    Number(productionSheet.bagDetails.input.flaps.upperFlapOpen.value) / 2 +
    Number(productionSheet.bagDetails.input.flaps.zipFlap.value) +
    Number(productionSheet.bagDetails.input.gussets.openBottomGusset.value) / 2;

  const bareWeight = (
    widthDevelopment *
    heightDevelopment *
    2 *
    (Number(productionSheet.bagDetails.output.thickness.value) / 10000) *
    Number(productionSheet.bagDetails.output.specificWeight.value)
  ).toFixed(2);

  const netDieCutWeight = (
    widthDevelopment *
    heightDevelopmentNet *
    2 *
    (Number(productionSheet.bagDetails.output.thickness.value) / 10000) *
    Number(productionSheet.bagDetails.output.specificWeight.value) *
    (1 - Number(productionSheet.bagDetails.input.cutting.cuttingDie.value) / 100)
  ).toFixed(2);
  

  console.log("Before bagweight: ", productionSheet.bagWeight);
  productionSheet.bagWeight = {
    input: {
      ...productionSheet.bagWeight.input,
    },
    output: {
      ...productionSheet.bagWeight.output, 
      bareWeight: { value: bareWeight, cost: false, unit: "g" },
      netDieCutWeight: { value: netDieCutWeight, cost: false, unit: "g" },
    },
  };
  console.log("After bagweight: ", productionSheet.bagWeight);

  return productionSheet; 
};

export default calculateWeightSingleBag;

I have this result from the console.log():

Before bagweight:  {
  input: { netDieCutWeight: { value: 0, cost: false, unit: 'g' } },
  output: {
    bareWeight: { value: 0, cost: false, unit: 'g' },
    netDieCutWeight: { value: 0, cost: false, unit: 'g' },
    netDieCutWeightWithAccessories: { value: 0, cost: false, unit: 'g' }
  }
}
After bagweight:  {
  output: {
    bareWeight: { value: 1.56, cost: false, unit: 'g' },
    netDieCutWeight: { value: 1.47, cost: false, unit: 'g' }
  }
}

After bagweight should be:

  input: { netDieCutWeight: { value: 0, cost: false, unit: 'g' } },
  output: {
    bareWeight: { value: 1.56, cost: false, unit: 'g' },
    netDieCutWeight: { value: 1.47, cost: false, unit: 'g' }
    netDieCutWeightWithAccessories: { value: 0, cost: false, unit: 'g' }
  }
}

Req.Body is:

const emptyProductionSheetBackEnd = {
  userId: "",

  information: {
    input: {
      customer: "",
      itemCode: "",
      description: "",
    },
  },

  bagDetails: {
    input: {
      bagType: { value: "Zip bag", cost: false },
      dimensions: {
        width: { value: 10, cost: false, unit: "mm" },
        height: { value: 15, cost: false, unit: "mm" },
      },
      gussets: {
        openBottomGusset: { value: 0, cost: false, unit: "mm" },
        openSideGusset: { value: 0, cost: false, unit: "mm" },
      },
      flaps: {
        zipFlap: { value: 0, cost: false, unit: "mm" },
        upperFlapOpen: { value: 2, cost: false, unit: "mm" },
      },
      cutting: {
        cuttingDie: { value: 0, cost: false, unit: "mm" },
      },
    },
    output: {
      thickness: {
        value: 50,
        cost: false,
        unit: "microns",
      },
      specificWeight: {
        value: 0.92,
        cost: false,
        unit: "g/cm",
      },
    },
  },

  bagWeight: {
    input: {
      netDieCutWeight: { value: 0, cost: false, unit: "g" },
    },
    output: {
      bareWeight: { value: 0, cost: false, unit: "g" },
      netDieCutWeight: { value: 0, cost: false, unit: "g" },
      netDieCutWeightWithAccessories: { value: 0, cost: false, unit: "g" },
    },
  },

};

Share Improve this question edited Feb 4 at 8:37 Alessio Canna asked Feb 3 at 19:46 Alessio CannaAlessio Canna 12 bronze badges 7
  • Can you provide the sample input as well. What is the value of req.body – search-learn Commented Feb 3 at 20:19
  • If you just need to assign two properties, do that. Don't assign a property a completely object with content that you destructure from the original object. That's... super weird. – Mike 'Pomax' Kamermans Commented Feb 3 at 20:33
  • Maybe your ProductionSheet class is doing something weird with a setter for the bagWeight property. – James Commented Feb 3 at 20:45
  • Note that by calling toFixed(), value is a string value. This may be acceptable, but the log is lying: bagweight.output.bareWeight.value will be "1.56" not 1.56. Use Number(value) or the unary plus operator (+value) to pass an actual number via JSON. – Heretic Monkey Commented Feb 3 at 21:13
  • I think it's worth reducing the amount of code in this question to the minimum. As people trying to answer your question, we shouldn't have to figure out the details for your business - we shouldn't care if it's a productionSheet or a banana. This is also a problem with the way the code is written - the function to calculate bag weight has to know about a production sheet is. If you reduce the amount of code in the question, you'd probably realize what the problem was, and be able to make the code more readable/sustainable, as well. Recomment posting in codereview.stackexchange – Codebling Commented Feb 3 at 21:20
 |  Show 2 more comments

1 Answer 1

Reset to default 3

Make your life easier: refactor your code so you're not repeating the same thing a million times, so you can actually work with it and understand it at a glance.

It also makes it much easier to change something that needs changing. In this case, the way you added those new properties: just add them. Don't create a completely new object with destructured "identical content", just direct, plain JS property assignment.

// This *absolutely* doesn't need to be a const, just declare it as
// a function that you can load from whatever is your `utils` module.
function calculateSingleBagWeight(productionSheet) {
  const { bagWeight, input, output } =  productionSheet.bagDetails;
  const { dimensions, cutting, gussets, flaps } = input;

  // Get all these strings converted to numbers.
  const [
    width,
    height,
    cuttingDie,
    openSideGusset,
    openBottomGusset,
    upperFlapOpen,
    zipFlap,
    thickness,
    specificWeight,
  ] = [
    dimensions.width,
    dimensions.height,
    cutting.cuttingDie,
    gussets.openSideGusset,
    gussets.openBottomGusset,
    flaps.upperFlapOpen,
    flaps.zipFlap,
    output.thickness,
    output.specificWeight,
  ].map(e => parseFloat(e.value));

  // Do some basic math:
  const widthDevelopment = width + openSideGusset;
  const heightDevelopment = height + upperFlapOpen + zipFlap + openBottomGusset / 2;
  const heightDevelopmentNet = height + upperFlapOpen / 2 + zipFlap + openBottomGusset / 2;

  const commonFactor = widthDevelopment * 2 * thickness / 10000  * specificWeight;
  const bareWeight = heightDevelopment * commonFactor;
  const dieFactor = (1 - cuttingDie) / 100;
  const netDieCutWeight = heightDevelopmentNet * commonFactor * dieFactor;
 
  // If all you want is to add properties to `output`: do that.
  // Only write code for what needs to happen. Anything else is
  // just a place for bugs to hide.

  // step 1: make sure "output" exists. If it does, this line does nothing.
  bagWeight.output ??= {};

  // step 2: just assign the property values you need.
  // And note: never use .toFixed() until the absolute last moment.
  // Which is here. Not in the calculations we did before.
  bagWeight.output.bareWeight = {
    value: bareWeight.toFixed(2),
    cost: false,
    unit: `g`
  };

  bagWeight.outputDieCutWeight = {
    value: netDieCutWeight.toFixed(2),
    cost: false,
    unit: `g`
  };

  return productionSheet;
}
发布评论

评论列表(0)

  1. 暂无评论