Home > Software engineering >  How to use a negated every(), or just some(), in Javascript to check that a key is missing from ever
How to use a negated every(), or just some(), in Javascript to check that a key is missing from ever

Time:10-07

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:

  • Code loops and I don't want it to throw 300 times, so find() is not an option. Not confident that breaking loop allows it to properly iterate.
  • every() seems like the right fit for these preconditions, but I don't know how to negate it.
  • some() might work, since it stops checking after it finds one targetKey, but it refused to work for me at all. Could have been programming error.
  • indexOf(), but it was always returning -1, even when the response returned. Again, could have been improperly programmed.

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.

CodePudding user response:

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()

CodePudding user response:

Since you need to check if anyone object has key, you can do a string search.

Logic:

  • Create a regex to test string. Pattern would be "<key>":
  • Then use JSON.stringify and test if this object has this key in any specific level

The benefit of this approach is that you can do it for deeply nested objects without knowing/ navigating to a specific path. Con is that if you have a specific path, this will need tweeking and beats the purpose of not looping

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") => {
  return new RegExp(`"${prop}":`, ['gi']).test(JSON.stringify(categories))
}

console.log('WithTargetKey: ', detectProp(withTargetKey))

console.log('WithoutTargetKey: ', detectProp(withoutTargetKey))

CodePudding user response:

A functional approach is very straightforward when you can phrase your goal.

We need to know if in every category and in every product the targetKey is not (here you negate) included.

Note that negating product.targetKey can produce the wrong output when its value is falsy (null, undefined, 0). So it is not a safe way to check for existing keys.

This is another approach to @Phil's that considers using Object.keys combined with Array.prototype.includes instead of Object.hasOwn and nested every instead of some.

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

const isValid = (responseType) =>
  responseType.categories.every(category => 
    category.products.every(product => !Object.keys(product).includes("targetKey")))

console.log(isValid(responseRegular), isValid(responseTarget))

  • Related