0

I have a Javascript object that can have either none, one, or multiple keys. Then each key has a nested value.

Example

cart = {
    "98eb9514-f403-4d08-b5f7-dd6a5ec013cb": {
        "quantity": 1,
        "FirstName": "John",
        "LastName": "Lewis"
    },
    "92ef2918-6bc2-4f3b-b9b3-acf6ebe74b1f": {
        "quantity": 1,
        "FirstName": "Matthew",
        "LastName": "Smith"
    }
}

I need to check if a key has an exact nested value. If it does, then I need to add 1 to the quantity for that exact key with that exact nested value.

I am able to check if an exact key with that exact nested value exist by doing:

if (cart.hasOwnProperty(productId) &&
    cart[productId]["quantity"] >= 1 &&
    cart[productId]["FirstName"] == FirstName &&
    cart[productId]["LastName"] == LastName) {
    console.log('add one to qty somehow...')
}

However, this seems very inefficient and then I can't even figure out a way to add one to the quantity for just that exact key with that exact nested value.

If it is not clear, my question comes down to this: How do I check if a key with an exact nested value exist, and if it does, how do I add 1 to the quantity for that same exact key/nested value.

Been working on this for a day and half. Any help is much appreciated.

AbsoluteBeginner
  • 2,160
  • 3
  • 11
  • 21
pycode81
  • 124
  • 1
  • 9
  • You're essentially asking [this](https://stackoverflow.com/questions/201183/how-to-determine-equality-for-two-javascript-objects). You're doing it the technically correct way; in your situation you would probably be able to use the simpler "compare JSON.stringified versions of the object" method instead. – Daniel Beck Oct 15 '21 at 15:33
  • Why not use one of any libraries that comes with a "deep equals" function? (If you're working in Node: good news, you already get `assert` for free, it's part of the Node API) That's been a solved problem for a long time now, no need to roll your own code here =) – Mike 'Pomax' Kamermans Oct 15 '21 at 15:34
  • What do you mean by "exact nested value"? A certain set of properties with a certain value? If that's the case, then there is pretty much no way around comparing each and every property. There might be some libraries implementing a generic deep object equality (`==` won't work!) But they are just hiding the same concept behind a function call – derpirscher Oct 15 '21 at 15:34
  • `cart[productId].quantity++` would add one to the quantity prop in the nested value. – MinusFour Oct 15 '21 at 15:38
  • @DanielBeck comparing stringified objects as equality check may be quite dangerous, as the result of a stringify *may* depend on the order in which the properties are inserted in the object. (Depending on the particular runtime) – derpirscher Oct 15 '21 at 15:42
  • If you want schema validation, consider using [joi](https://joi.dev/api/). – jarmod Oct 15 '21 at 15:49
  • Strictly speaking @derpischer is correct; as far as I known current JS runtimes all have a deterministic JSON.stringify, but it's still probably not a great idea to depend on it, I probably shouldn't have suggested it. – Daniel Beck Oct 15 '21 at 15:54
  • Except you can _force_ the ordering to be the same: JSON.stringify doesn't take one argument, [it takes three](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#syntax), and the second argument is what allows this solution to work perfectly fine, every single time. The `replacer` argument for JSON.stringify is a rarely talked about powerhouse for object rewriting/analysis. https://stackoverflow.com/a/69587784/740553 – Mike 'Pomax' Kamermans Oct 15 '21 at 16:26

4 Answers4

1

You could do it like this:

const cart = {
"98eb9514-f403-4d08-b5f7-dd6a5ec013cb":{"quantity":1,"FirstName":"John", "LastName":"Lewis"},
"92ef2918-6bc2-4f3b-b9b3-acf6ebe74b1f":{"quantity":1,"FirstName":"Matthew", "LastName":"Smith"}
}
const productId = "92ef2918-6bc2-4f3b-b9b3-acf6ebe74b1f";
const firstName = "Matthew";
const lastName = "Smith";
const quantity = 1;

const cartItem = cart[productId];

if(typeof cartItem !== "undefined" && cartItem.FirstName === firstName && cartItem.LastName === lastName && cartItem.quantity >= 1) {
  cartItem.quantity += 1;
};

console.log(cart);

Once you have a handle on the intended cart property, you can simply increment its quantity property.

Mario Varchmin
  • 3,704
  • 4
  • 18
  • 33
0

let cart = {
    "98eb9514-f403-4d08-b5f7-dd6a5ec013cb": {
        "quantity": 1,
        "FirstName": "John",
        "LastName": "Lewis"
    },
    "92ef2918-6bc2-4f3b-b9b3-acf6ebe74b1f": {
        "quantity": 1,
        "FirstName": "Matthew",
        "LastName": "Smith"
    }
}
let productId = '98eb9514-f403-4d08-b5f7-dd6a5ec013cb'; // any product key
let valueToMAtch = 1; // any value to match

let productKey = Object.keys(cart).find((key) => key === productId);
Object.entries(cart[productKey] || {}).forEach(([key, value]) => { // || {} allows undefined handing if no product is found
    if (value === valueToMAtch) {
        cart[productKey][key] += 1; // adding one
    }
});

console.log(cart); // result
vaira
  • 2,191
  • 1
  • 10
  • 15
0

If you're comparing thousands of records to thousands of other records, might want to pack it up somehow - the intent here, though, is it to de-dupe?

If so, you could try something like...

var cartCounter = {};
for (var itemKey in cart) {
 var item = cart[itemKey];
 var key = item.FirstName + "|" + item.LastName;
 if (!cartCounter[key]) {
  cartCounter[key] = item;
 } else {
  cartCounter[key].quantity++;
 }
}
0

If you need deep equality based on enumerable properties only (that is: the values that you get when you use for...of, or Object.keys, etc.) then you can determine equality based on the JSON serialization of those objects, provided you force key sorting, using the rarely talked about replacer argument for JSON.stringify().

This is an incredibly powerful feature of the JSON serialization process, and we can write a simple function that will guarantee any object we pass in will always serialize to the same JSON string, regardless of key ordering at any depth:

function sortedObjectKeys(_, data) {
  // Ignore primitives.
  if (data === null) return null;
  if (typeof data !== "object") return data;

  // Also ignore iterables, which are type "object" but
  // should not be sorted because [1,2,3] is not [1,3,2].
  if (data.__proto__[Symbol.iterator]) return data;

  // Then sort the object keys and yield a new object with that
  // ordering enforced by key insertion.
  return Object.fromEntries(Object.entries(data).sort());
}

We can now perform a deep equality test using string equality:

function deepEqual(o1, o2) {
  const s1 = JSON.stringify(o1, sortedObjectKeys);
  const s2 = JSON.stringify(o2, sortedObjectKeys);
  return s1 === s2;
}
Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153