javascriptarraysjsonmethodsnegation

How to use a negated every(), or just some(), in Javascript to check that a key is missing from every iteration?


Having trouble wrapping my mind around negating an every() statement in js. Can someone help me out?

Here's the data/response I need to work with:

{
    "categories": [
        {
            "categoryName": "category1",
            "products": [
                {
                    "name": "product1",
                    "targetKey": {
                        "bananas": "are good"
                    }
                },
                {
                    "name": "product2"
                }
            ]
        },
        {
            "categoryName": "category2",
            "products": [
                {
                    "name": "product3"
                },
                {
                    "name": "product4",
                    "targetKey": {
                        "bananas": "are good"
                    }
                }
            ]
        }
    ]
}

I want to execute code ONLY WHEN NO !targetKey is found (at all, anywhere), and I only want to execute it ONCE, not per iteration. If even one targetKey is present in the response, this cannot return.

The above response would look like this, if the case was true:

{
    "categories": [
        {
            "categoryName": "category1",
            "products": [
                {
                    "name": "product1"
                },
                {
                    "name": "product2"
                }
            ]
        },
        {
            "categoryName": "category2",
            "products": [
                {
                    "name": "product3"
                },
                {
                    "name": "product4"
                }
            ]
        }
    ]
}

What I tried:

Here's what I want to know:

1. How (or, where?) do I properly negate every(), so that the language makes the right checks?

Even if it's the wrong method to use, can it be done and what is the proper place for the negation? Are either of these correct? Both seem equally happy, but I'm guessing one or both are completely wrong.

for (const category of response.categories) {
    if (Object.keys(category.products).every(product => !product.targetKey)) {
        throw new Error("targetKey is missing!");
    };
}
for (const category of response.categories) {
    if (!Object.keys(category.products).every(product => product.targetKey)) {
        throw new Error("targetKey is missing!");
    };
}

2. I'm guessing these don't mean the same thing. Can you explain the difference?

3. If I do need to use if some() + else{} what does that code look like?

Sorry for the long post and many thanks.


Solution

  • Nesting Array.prototype.some() and negating the result will tell you when the property is not present.

    const withTargetKey = {"categories":[{"categoryName":"category1","products":[{"name":"product1","targetKey":{"bananas":"are good"}},{"name":"product2"}]},{"categoryName":"category2","products":[{"name":"product3"},{"name":"product4","targetKey":{"bananas":"are good"}}]}]};
    const withoutTargetKey = {"categories":[{"categoryName":"category1","products":[{"name":"product1"},{"name":"product2"}]},{"categoryName":"category2","products":[{"name":"product3"},{"name":"product4"}]}]};
    
    const detectProp = ({ categories }, prop = "targetKey") =>
      categories.some(({ products }) =>
        products.some((product) => Object.hasOwn(product, prop))
      );
    
    if (!detectProp(withTargetKey)) {
      console.log("withTargetKey does not have any targetKeys"); // won't see this
    }
    
    if (!detectProp(withoutTargetKey)) {
      console.log("withoutTargetKey does not have any targetKeys");
    }

    See also Object.hasOwn()