If I have an object, such as:
const obj = {
field1: {
subfield1: true,
subfield2: true,
},
field2: {
subfield3: true,
},
field3: {
subfield4: false,
subfield5: true,
}
}
Then I can do a nested for loop to return true if a value is false
by doing,
const findFalse = obj => {
for (const field in obj) {
for (const subfield in obj[field]) {
if (!obj[field][subfield]) return true
}
}
}
If this object wasn't nested, it'd be a bit easier, I could do something like,
const findFalse = obj => Object.keys(obj).some(prop => !obj[prop]);
Both approaches don't really suit what I need, because the object can have 2, 3 or more nested properties. I've come up with a recursive way to figure this out, such as:
const isObj = obj => obj && obj.constructor === Object;
const findFalseRecursively = obj => {
for (const field in obj) {
if (isObj(obj[field]))
return findFalseRecursively(obj[field])
if (obj[field] === false)
return true
}
}
But I'm wondering if there's a cleaner or more efficient way of achieving this?
I would expect the function to return the first false
value that it sees, and break out of the function/for loop instantly.
If I have an object, such as:
const obj = {
field1: {
subfield1: true,
subfield2: true,
},
field2: {
subfield3: true,
},
field3: {
subfield4: false,
subfield5: true,
}
}
Then I can do a nested for loop to return true if a value is false
by doing,
const findFalse = obj => {
for (const field in obj) {
for (const subfield in obj[field]) {
if (!obj[field][subfield]) return true
}
}
}
If this object wasn't nested, it'd be a bit easier, I could do something like,
const findFalse = obj => Object.keys(obj).some(prop => !obj[prop]);
Both approaches don't really suit what I need, because the object can have 2, 3 or more nested properties. I've come up with a recursive way to figure this out, such as:
const isObj = obj => obj && obj.constructor === Object;
const findFalseRecursively = obj => {
for (const field in obj) {
if (isObj(obj[field]))
return findFalseRecursively(obj[field])
if (obj[field] === false)
return true
}
}
But I'm wondering if there's a cleaner or more efficient way of achieving this?
I would expect the function to return the first false
value that it sees, and break out of the function/for loop instantly.
4 Answers
Reset to default 13You could take the valus from the object and iterate with a short circuit.
const
hasTrue = object => Object
.values(object)
.some(v => v === false || v && typeof v === 'object' && hasTrue(v)),
object = { field1: { subfield1: true, subfield2: true }, field2: { subfield3: true }, field3: { subfield4: false, subfield5: true } };
console.log(hasTrue(object));
With suggested check object in advance
const
hasTrue = value => value === true || !!value && typeof value === 'object' && Object
.values(value)
.some(hasTrue),
object = { field1: { subfield1: true, subfield2: true }, field2: { subfield3: true }, field3: { subfield4: false, subfield5: true } };
console.log(hasTrue(object));
Here is a simple recursive solution that uses Object.values
,flat
and some
.
function toValues (obj) {
if (typeof obj === 'object') return Object.values(obj).map(toValues);
return obj;
}
const findFalse = obj => toValues(obj).flat().some(v => v === false)
const res = findFalse(obj);
console.log (res);
<script>
const obj = {
field1: {
subfield1: true,
subfield2: true,
},
field2: {
subfield3: true,
},
field3: {
subfield4: false,
subfield5: true,
}
}
</script>
You could do it by searching inside the string representation of the object like so:
const o = {
field1: {
subfield1: true,
subfield2: true,
},
field2: {
subfield3: true,
},
field3: {
subfield4: false,
subfield5: true,
}
};
const containsFalse = (obj) => /\".+\"\:false[^\"]/.test(JSON.stringify(obj));
console.log(containsFalse(o));
console.log(containsFalse({a:":false"}));
JSON.stringify(...)
is pretty costly though, so I don't know if this is an improvement performance wise.
Update: It is a bit slower than other methods shown here, but if you need something simple it might still be useful. https://jsbench.me/4akcx6gxsn/1
If there's arbitrary nesting, you can't avoid recursion. You can make it cleaner though:
const isObj = obj => obj && typeof obj == "object";
const findFalseRecursively = val => {
if (val === false)
return true;
if (isObj(val))
for (const field in val) {
if (findFalseRecursively(val[field]))
return true;
return false;
}
true
/false
? or all withfalse
? please add the wanted result. – Nina Scholz Commented Jul 22, 2020 at 9:18false
, just that he wants to detect, whether an object contains a value that isfalse
. So my guess is: on the firstfalse
, returntrue
. – MauriceNino Commented Jul 22, 2020 at 9:21return false
at some point? – Teepeemm Commented Jul 22, 2020 at 17:43