how to check if all object keys has false values - javascript

JS Object:
var saver = {
title: false,
preview: false,
body: false,
bottom: false,
locale: false
};
The question is how to check if all values is false?
I can use $.each() jQuery function and some flag variable, but there may be a better solution?

Updated version. Thanks #BOB for pointing out that you can use values directly:
Object.values(obj).every((v) => v === false)
Also, the question asked for comparison to false and most answers below return true if the object values are falsy (eg. 0, undefined, null, false), not only if they are strictly false.
This is a very simple solution that requires JavaScript 1.8.5.
Object.keys(obj).every((k) => !obj[k])
Examples:
obj = {'a': true, 'b': true}
Object.keys(obj).every((k) => !obj[k]) // returns false
obj = {'a': false, 'b': true}
Object.keys(obj).every((k) => !obj[k]) // returns false
obj = {'a': false, 'b': false}
Object.keys(obj).every((k) => !obj[k]) // returns true
Alternatively you could write
Object.keys(obj).every((k) => obj[k] == false)
Object.keys(obj).every((k) => obj[k] === false) // or this
Object.keys(obj).every((k) => obj[k]) // or this to return true if all values are true
See the Mozilla Developer Network Object.keys()'s reference for further information.

This will do the trick...
var result = true;
for (var i in saver) {
if (saver[i] === true) {
result = false;
break;
}
}
You can iterate objects using a loop, either by index or key (as above).
If you're after tidy code, and not repeating that then simply put it in a function...
Object.prototype.allFalse = function() {
for (var i in this) {
if (this[i] === true) return false;
}
return true;
}
Then you can call it whenever you need, like this...
alert(saver.allFalse());
Here's a working sample...
Object.prototype.allFalse = function() {
for (var i in this) {
if (this[i] === true) return false;
}
return true;
}
var saver = {
title: false,
preview: false,
body: false,
bottom: false,
locale: false
};
console.log("all are false - should alert 'true'");
console.log(saver.allFalse());
saver.body = true;
console.log("one is now true - should alert 'false'");
console.log(saver.allFalse());

In a comment you ask if you can avoid iteration. You can if you use a javascript library supporting a functional approach, like Underscore, Lodash or Sugar.
With Underscore and Lodash you can write something like this:
var result = _.every(_.values(saver), function(v) {return !v;});
With Sugar you can simply write:
var result = Object.all(saver,false);

Use array.some()
It's more clean and understandable! And it can save us running time, because once the function condition exist once, it goes out of the loop and returns true.
Object.values(obj).some(val => val)
if you actually need strict equality to false write this:
Object.values(obj).some(val => val !== false)
Object.values(obj) make an array with the values of each key.

Short and handy one-liner, fully supported by browsers:
Object.keys(saver).every(k => saver[k] === false);
or
Object.values(saver).every(v => v === false);
(careful tho, Object.values() is not supported by IE yet.)

This should work on all major browsers:
Object.keys(saver).every(key => saver[key] === false); // return true

Do like this,
for (var i in saver) {
if (saver[i]) {
return false; // here if any value is true it wll return as false /
}
}
return true; //here if all value is false it wll return as true

If you want to do it without external iteration (i.e. in your code), try mapping the properties to an array with $.map then using $.inArray to see if any true values exist:
var allFalse = $.inArray(true, $.map(saver, function(obj){return obj})) < 0;
JSFiddle: http://jsfiddle.net/TrueBlueAussie/FLhZL/1/

With lodash you could also do const allFalse = !_.some(saver);

Lodash (3.10.1+) makes this even cleaner to express explicitly:
_.every({a: false, b: false, c: false}, _.negate(Boolean)); // True
But using _.some per ngstschr's answer is more succinct.

✏️ This one-liner checks if there's a falsy value inside any object of an array of objects:
const hasFalsyValue = (list) =>
!list.every(obj => Object.values(obj).every(prop => prop))
I find this one useful to prevent null / falsy values from being passed further on:
const listA = [ { a:'🍎', b:100 }, { a:'🍌', b:200 } ]
const listB = [ { a:null, b:100 }, { a:'🍌', b:200 } ]
// hasFalsyValue(listA) === false
// hasFalsyValue(listB) === true
There's just one detail we should be aware of:
⚠️ In Javascript 0 and '' are Falsy values! ⚠️
So if hasFalsyValue() finds any zero value or any empty string value inside an object in the array, it will consider it a falsy value and thus return true!
...While this might be what you want, sometimes you might want to allow any particular Falsy value to be considered as Truthy.
✍🏽 Say you want to allow zero values in your objects, you can do the following:
const hasFalsyValue_ZeroAllowed =
(list) => !list.every(obj => Object.values(obj).every(prop => prop || prop === 0))
Now zero values won't be considered falsy anymore:
const listC = [ { a:0, b:100 }, { a:'🍌', b:200 } ]
const listD = [ { a: null, b:100 }, { a:'🍌', b:200 } ]
hasFalsyValue_ZeroAllowed(listC) // false
hasFalsyValue_ZeroAllowed(listD) // true
And you can keep on adding further conditions to the function for a tailored validation:
✍🏽 To allow null values:
const hasFalsyValue_NullAllowed =
(list) => !list.every(obj => Object.values(obj).every(prop => prop || prop === null))
const listE = [ { a: null, b:100 }, { a:'🍌', b:200 } ]
hasFalsyValue_NullAllowed(listE) // false
✍🏽 To allow empty string values:
const hasFalsyValue_EmptyStringAllowed =
(list) => !list.every(obj => Object.values(obj).every(prop => prop || prop === ''))
const listF = [ { a: '', b:100 }, { a:'🍌', b:200 } ]
hasFalsyValue_EmptyStringAllowed(listF) // false

As of Lodash 4.0, overEvery can be used
overEvery(saver, false) loops through every element & checks if its false
It returns true if every element is false otherwise returns false

Related

What's the most efficient way to compare values of two objects?

Let's say I have two objects, which might have some properties in common. The overall idea is that one is used as a schema to compare it to the other. I would like to compare values of common properties and construct a new object representing these comparisons of each individual property. In the result, any properties in common should have the value of comparing the source properties for equality (e.g. result.a.b.c = (obj1.a.b.c == obj2.a.b.c)). Any properties that exist on only one of the two objects would be copied to the result with their original values.
For example, consider the following objects to compare:
const schema = {
num: 123,
str: 'hello',
nested: {
num: 123,
str: 'hello',
},
},
doc = {
nums: [1,2,3],
str: 'hello',
nested: {
num: 123,
str: 'world',
foo: 'bar',
},
};
The comparison result should be:
{
nums: [1,2,3], // obj2.nums
num: 123, // obj1.num
str: true, // obj1.str == obj2.str
nested: {
num: true, // obj1.nested.num == obj2.nested.num
str: false, // obj1.nested.str == obj2.nested.str
foo: 'bar', // obj2.nested.foo
},
}
How would I perform this comparison in the most efficient time and space way?
Currently I have this implementation:
function deepCompare(obj1, obj2) {
const validatedObject = {}
Object.keys(obj1).forEach(e => {
if(object[e].constructor.name === 'Object') {
validatedObject[e] = deepCompare(obj1[e], obj2[e])
} else {
validatedObject[e] = obj1[e] === obj2[e]
}
})
return validatedObject
}
Are there any ways to do this more efficiently without using JSON.stringify, because I might have property that is missing, but I still want to return the fact that the values are correct despite the schema of those objects?
Maybe for in or for of loop? But how would I test their effectiveness? It's okay for small objects, but sometimes they get really big and I want to compare them in the most efficient way possible.
NOTE for editors.
I understand that this might seem like a duplicate, but it in fact is not, here I'm looking for a most efficient way to compare values, iteratively, recursively or some other way, not objects themselves
the logic is if obj1 has a key and obj2 has the same key, we compare the values of those keys
Then you should use
function compare(a, b) {
if (a === b) return true;
if (typeof a == 'object' && a != null && typeof b == 'object' && b != null) {
return Object.keys(a).every(k =>
!(k in b) || compare(a[k], b[k])
);
}
return false;
}
I think your solution is good enough, it's only missing some default checks (you don't need to iterate through the objects keys if you have the same refference for example).
Also, there's this package that claims is the fastest deep equal checker.
You can look over the code to see if you missed any other condition (it's still a recursive solution for the objects, but there are some other data types treated there if you're interested).
There is also this article if you prefer written material instead of code that has more checks before recursive calls
What you want to do is first find the common object keys. Then as you loop through the properties you want to return false as soon as you find a mismatch. In the snippet below I'm using an every loop which will stop as soon as one iteration returns false. If you return a value in the if block you do not need the else block.
function deepCompare(obj1, obj2) {
const commonKeys = Object.keys(obj1).filter(x => Object.keys(obj2).includes(x));
let valid = true;
commonKeys.every(e => {
if (obj1[e].constructor.name === 'Object') {
console.log('object', e, obj1[e], obj2[e])
valid = deepCompare(obj1[e], obj2[e])
return valid // true goes to next, false ends the .every loop
}
console.log('not object', e, obj1[e] === obj2[e] ? 'match' : 'mismatch')
valid = obj1[e] === obj2[e]
return valid // true goes to next, false ends the .every loop
})
return valid // returns the first false or true if everything passes
}
const obj1 = {
num: 123,
str: 'hello',
nested: {
num: 123,
str: 'hello',
}
}
const obj3 = {
num: 1233,
str: 'hello'
}
const obj4 = {
num: 123,
str: 'hello',
nested: {
num: 123,
str: 'hellure',
}
}
console.log(deepCompare(obj1, obj1.nested))
console.log(deepCompare(obj1, obj3))
console.log(deepCompare(obj1, obj4))

Compare objects and remove equal values

I want to be able to compare two objects and remove the properties which values has not been changed.
prevObject = {
isActive: true,
userName: 'Patric',
settings: {
isUser: true
}
}
nextObject = {
isActive: true,
userName: 'Patric Surname',
settings: {
isUser: false
}
}
The result when comparing above should be:
{
userName: 'Patric Surname',
settings: {
isUser: false
}
}
I've tried some for in loops and such, but I haven't gotten it to work recursively. Sorry, I don't have any code to show that I've tried. I trashed it since I was a bit frustrated. I'm also stuck to pure ES5 on this one. So no lodash or underscore :)
Would be really glad for some help!
You'll need a recursive approach:
In ECMAScript-5 syntax:
function isObject(a) {
return typeof a === "object" && a;
}
function diff(a, b) {
var res = b instanceof Array ? [] : {};
for (var prop in b) {
if (!(prop in a) || (!isObject(a[prop]) || !isObject(b[prop])) && a[prop] !== b[prop]) {
res[prop] = b[prop];
} else {
var obj = diff(a[prop], b[prop]);
// If the recursive result is not an empty object, then use it
for (var _ in obj) {
res[prop] = obj;
break; // We only need 1 iteration
}
}
}
return res;
}
var prevObject = {isActive: true,userName: 'Patric',settings: {isUser: true }};
var nextObject = { isActive: true,userName: 'Patric Surname',settings: {isUser: false}};
console.log(diff(prevObject, nextObject));
This function has some limited support for arrays. Array elements are only considered the same if their content is the same and the index where they occur in the array. This means that a resulting array may have gaps ("sparse").
var prevObject = {
isActive: true,
userName: 'Patric',
settings: {
isUser: true
}
};
var nextObject = {
isActive: true,
userName: 'Patric Surname',
settings: {
isUser: false
}
};
var changes = Object.entries(nextObject).reduce((acc,cv)=>{
if(JSON.stringify(nextObject[cv[0]]) != JSON.stringify(prevObject[cv[0]]))
acc.push(cv);
return acc;
},[]).reduce((acc,cv)=>{
acc[cv[0]]=cv[1];
return acc;
},{});
console.log(changes);
Assuming both prevObject and nextObject have the same properties.
Object.entries of nextObject returns an Array of [key, value]s.
Initialize return value of reduce on that Array to an empty Array - [].
Fill it with properties if their JSON.stringify are different.
JSON.stringify comparison relieves us from checking type, etc. For example: [1,2,3] is NOT equal [1,2,3], but JSON.stringify([1,2,3])==JSON.stringify([1,2,3]) IS. Same for objects, etc.
We use reduce on the last created Array using {} as initial value, filling with properties (cv[0]) and their values (cv[1]) back into an Object.

JS - Get object key-value pairs for filtered values WITHOUT underscore

I have a problem with filtering object's values by true/false, just like in this topic Get object keys for filtered values but without underscore, meaning using exclusively plain JS. There are many more or less similar questions on StackOverflow, but unfortunately I failed to set a working approach.
For example, I have an object:
var anObject = {first_property: false, second_property: true, third_property: false, fourth_property: false, fifth_property: false, sixth_property: true, seventh_property: false, eight_property: nine-nth_property: false}
I need to get a new object, featuring exclusively truthy values, like:
var newObject = { second_property: true, sixth_property: true }
To filter, I wrote the following filter:
function isValid(value) {
if (typeof value === "undefined" || value == null || value.length == 0 || value == false) {
return false;
} else {
return true;
}
}
Broke my head and spent a few hours trying different approach, however the result is unsatisfactory. How should I built the algorithm to do this, which custom/out-of-the-box functions is worth using here? Thanks in advance!
You need to loop over the key/value pairs and find out which are true. The solution works as follows:
Gets the key/value pairs from the object using Object.entries.
Iterates over the entries using Array.reduce.
In each iteration, checks if value is true. If so, then adds the key/value pair to the result.
var obj = {
first_property: false,
second_property: true,
third_property: false,
fourth_property: false,
fifth_property: false,
sixth_property: true,
seventh_property: false
};
var res = Object.entries(obj)
.reduce((result, [key, value]) => {
if (value) {
result[key] = value;
}
return result;
}, {})
console.log(res);
There are a few ways to do this using Object.entries() or Object.keys(), different array methods, etc, but I figured I'd provide one. Filter the keys down to those where the value is true, and then add those keys to the output.
var obj = {
first_property: false,
second_property: true,
third_property: false,
fourth_property: false,
fifth_property: false,
sixth_property: true,
seventh_property: false
};
var output = {};
Object.keys(obj) //Get all keys from the object ['first_property', 'second_property', ...]
.filter((key) => obj[key]) //Filter the list of keys down to only those where their value is true
.forEach((key) => output[key] = obj[key]); //For each key in our filtered list, add it to the output object
console.log(output);
Also it is possible by reducing Object.keys array (without additional filtering):
Object.keys(obj).reduce((acc, key) =>
((obj[key] ? acc[key] = obj[key] : null), acc)
, {});
// {second_property: true, sixth_property: true}
And a bit shorter version:
Object.keys(obj).reduce((acc, key) => (obj[key] && (acc[key] = obj[key]), acc), {});
You can filter (Array.prototype.filter) the truthy keys and create a new object (Array.prototype.reduce) with those:
var obj = {first_property: false,second_property: true,third_property: false,fourth_property: false,fifth_property: false,sixth_property: true,seventh_property: false};
var result = Object.keys(obj).filter(k => obj[k]).reduce((a,k) => (a[k] = obj[k], a), {});
console.log(result);

Javascript ES2015 check if one of the named key in an object is empty

I have an array that looks like below
process:Array[3]
0:Object
office_id:""
1:Object
office_id:6
2:Object
office_id:""
I want to check if office_id named key is not empty
If I find at least one office_id that is not empty it will return true else if all is empty it will return false.
The default value of office_id is empty string as you can see.
The object is dynamic since I'm using some input select form to add another object with office_id so if they select something another object will be added with office_id equal to that specific select id.
Now, as for validation purpose I need to check if the object process contains an office_id with a numeric value.
Using simple for loop and more efficient way
function () {
for(i = 0; i < process.length; i++){
if (process[i].office_id){
return true;
break;
}
}
return false;
}
Using ES5 filter
function () {
var filteredProcess = process.filter(function(pr) {
if (pr.office_id){
return true;
}
});
return filterProcess.length ?
true:
false;
}
You can use some combined with the javascript truthy-ness to determine if it's empty. Since 0 evaluates to falsey, you need to handle that as a special case. See isEmpty below
const isEmpty = (arr) => arr.some(item => item.office_id === 0 || !!item.office_id);
// This returns false because all the elements are falsey
console.log(isEmpty([
{
office_id: "",
},
{
office_id: undefined,
},
{
office_id: null,
},
{
office_id: "",
},
]));
// This returns true because the number 6
console.log(isEmpty([
{
office_id: "",
},
{
office_id: 6,
},
]));
// This returns true because 0 is special cased
console.log(isEmpty([
{
office_id: "",
},
{
office_id: 0,
},
]));
Use Array.prototype.some to verify that at least one member of the array matches your condition:
const result = yourArray.some(item => item !== '')

Replace object values lodash

Lets say I have an object
var users = [
{ Mike: 'true' },
{ Tony: 'True' },
{ Ismael: 'RU' }
];
I have this problem where I want to normalise my object, basically replace "true" or "True" with a boolean true anything else should be false.
By the way I may have the syntax for users wrong, chrome is telling me users.constructor == Object and not Array.
How can I achieve this using lodash?
In Lodash, you can use _.mapValues:
const users = [
{ Mike: 'true' },
{ Tony: 'True' },
{ Ismael: 'RU' },
];
const normalisedUsers = users.map(user =>
_.mapValues(user, val => val.toLowerCase() === 'true')
);
console.log(normalisedUsers);
<script src="https://cdn.jsdelivr.net/lodash/4.16.3/lodash.min.js"></script>
You don't have to use lodash. You can use native Array.prototype.map() function:
const users = [
{ Mike: 'true' },
{ Tony: 'True' },
{ Ismael: 'RU' },
];
const normalisedUsers = users.map(user =>
// Get keys of the object
Object.keys(user)
// Map them to [key, value] pairs
.map(key => [key, user[key].toLowerCase() === 'true'])
// Turn the [key, value] pairs back to an object
.reduce((obj, [key, value]) => (obj[key] = value, obj), {})
);
console.log(normalisedUsers);
Functional programming FTW!
You don't need lodash to achieve this, just use a for loop. Here is an example
var users = [
{ Mike: 'true' },
{ Tony: 'True' },
{ Ismael: 'RU' }
];
for (key in users) {
if (users[key] == 'true' || users[key] == 'True') {
users[key] = true
} else {
users[key] = false
}
}
What this is doing is, if your value is 'true' or 'True' then assign a boolean val of true to your object & else assign false.
Edit
You could also do it the shorthand way, as suggested by 4castle:
users[key] = users[key] == 'true' || users[key] == 'True';
Simply replace the if/else block with this one line.

Categories

Resources