Filtering object properties based on value - javascript

Is there some elegant way of filtering out falsey properties from this object with lodash/underscore? Similar to how _.compact(array) removes falsey elements from arrays
so from
{
propA: true,
propB: true,
propC: false,
propD: true,
}
returning
{
propA: true,
propB: true,
propD: true,
}

Here are two vanilla javascript options:
A.: Iterate over the object's keys and delete those having a falsey value.
var obj = {
propA: true,
propB: true,
propC: false,
propD: true,
};
Object.keys(obj).forEach(key => {
if (!obj[key]) delete obj[key];
});
console.log(obj);
See Object.keys() and Array.prototype.forEach()
B.: Iterate over the object's keys and add truthy values to a new object.
var obj = {
propA: true,
propB: true,
propC: false,
propD: true,
};
var filteredObj = Object.keys(obj).reduce((p, c) => {
if (obj[c]) p[c] = obj[c];
return p;
}, {});
console.log(filteredObj);
See Object.keys() and Array.prototype.reduce()

Lodash 4.0
Lodash 4.0 has _.pick, which takes an array of properties, and _.pickBy which takes a function as an argument and returns an object only containing the keys for which that function returns truthy which is what we want here, so it'd be:
filtered = _.pickBy(obj, function(value, key) {return value;})
Or, since _.pickBy defaults to using _.identity as it's second argument, (and that's essentially what we've written above,) it can just be written as:
filtered = _.pickBy(obj);
Underscore or Lodash prior to version 4.0
In underscore and old versions of lodash, there's just a single _.pick, which has both behaviors of _.pick and _.pickWith from v4. So you can do:
filtered = _.pick(obj, function(value, key) {return value;})
Or more succinctly:
filtered = _.pick(obj, _.identity)

Unfortunately I cannot direclty comment on the posts above yet, so I create this extra post.
Since Lodash v4 the functionality described above has been moved to _.pickBy. With _.identity as default you could also change your code to:
var filtered = _.pickBy(obj);
See this JSBin for a working example.

As partial mentioned in a comment, ES6 provided Object.entries() and in 2019 Object.fromEntries().
Allowing:
Object.fromEntries(Object.entries(obj).filter(([key, value]) => ...))
Ex:
const obj = {
a: 12,
b: 123,
};
const filteredObj = Object.fromEntries(
Object.entries(obj).filter(
([_, value]) => value > 100
)
);
console.log(filteredObj);
// {b: 123}

If you're using lodash, I'd recommend something like this:
var object = {
propA: true,
propB: true,
propC: false,
propD: true,
};
_.pick(object, _.identity);
// →
// {
// propA: true,
// propB: true,
// propD: true
// }
The pick() function generates a new object that includes properties that the callback returns truthy for. So we can just use the identity() function as the callback, since it'll just return each property value.

From lodash 4, we can use pickBy() to get only the value equal to true.
const active = _.keys(_.pickBy(object));

let temp = {
propA: true,
propB: true,
propC: false,
propD: true,
}
let obj = {}
for(x in temp){
if(temp[x] == true){
obj[x] = temp[x]
}
}
console.log(obj)
Using for-in loop we can achieve it something like this.

Another approach
const objFilter = (obj, condition) => {
let newObj = {}
for (const [key, value] of Object.entries(obj)) {
if (condition(value)) {
newObj = { ...newObj, [key]: value }
}
}
return newObj
}
Fire like this:
const newData = objFilter(oldData, (value) => value.marked === false)

Related

Filtering an array using reduce

So I want to use Array.prototype.reduce function to reduce some array (converted from object).
This is the object which I'll later use Object.entries to get the array.
const activityLoading = {
topicsForClassCourse: true,
b: false,
c: false,
d: true,
e: false
}
I also have an array of keys that I want to ignore while reducing this object. So any key that are in activityLoading as well as local it will be neglected.
const local = [ "topicsForClassCourse", "e" ]
Now I want to reduce the object into a single value. If any key is true except the one in the local array, it should return true, else false.
This is what I could come up with. But it's returning false.
const loadingSome = () => {
const local = [ "topicsForClassCourse", "timetableForClass" ];
const entries = Object.entries(activityLoading)
const reducer = (acc, current) => {
if(local.includes(current[0])) {
return false
}
if(current[1]) {
return true;
}
return false
}
const result = entries.reduce(reducer, false)
console.log(result)
}
https://jsbin.com/nolobupeva/edit?js,console
Basically, I'd start w/ writing the test for a single value itself
function test(key, value) {
const ignore = local.includes(key)
return !ignore && value
}
Once you have that, there are many ways to run it on your entire object. And I'm not sure .reduce is the best way to go for this. I'd suggest going for .find
const activityLoading = {
topicsForClassCourse: true,
b: false,
c: false,
d: true,
e: false
}
const local = [ "topicsForClassCourse", "e" ]
const result = Object.entries(activityLoading).find(([key, value]) => {
const ignore = local.includes(key)
return !ignore && value
})
console.log(result) // ['d', true]
const foundSomething = !!result
console.log(foundSomething) // true
And if you find it cumbersome to deal with ['d', true] being returned, using .some instead of .find will return a boolean instead of the value that matched the test.
But if you really want to, .reduce could do the job too, just be careful with 2 things:
how you initialize it (second argument). It should start at false because initially you assume no value matches.
and how you accumulate (return). It should be return accumulator || current so that any true current will make the whole result true.
const activityLoading = {
topicsForClassCourse: true,
b: false,
c: false,
d: true,
e: false
}
const local = [ "topicsForClassCourse", "e" ]
const result = Object.entries(activityLoading).reduce((accu, [key, value]) => {
const ignore = local.includes(key)
const test = !ignore && value
return accu || test
}, false)
console.log(result) // true
You should try some more simple jobs using reduce function to take it in hand. Now for your issue you can try this one:
const loadingSome = () => {
const local = [ "topicsForClassCourse", "timetableForClass" ];
const entries = Object.entries(activityLoading)
const reducer = (acc, current) => {
if(local.includes(current[0])) {
return acc
}
return current[1] || acc;
}
const result = entries.reduce(reducer, false)
console.log(result)
}
As you see you forgot to take the acc into account. I have not try out the above code but it should work. If didn't let me know.
How about first filter the object to filter out the local array keys using filter and then use some to check any key is true/false.
const activityLoading = {
topicsForClassCourse: true,
b: false,
c: false,
d: true,
e: false
};
const local = ["topicsForClassCourse", "e"];
let result = Object.entries(activityLoading)
.filter(([key, value]) => !local.includes(key))
.some(([key, value]) => value);
console.log(result);
or you can use only some array helper
const activityLoading = {
topicsForClassCourse: true,
b: false,
c: false,
d: true,
e: false
};
const local = ["topicsForClassCourse", "e"];
let result = Object.entries(activityLoading)
.some(([key, value]) => !local.includes(key) && value)
console.log(result);

JS if key exist, include to array

I'm stuck in mapping object to array.
I use map, but its add every object field in array and i got a lot of undefined.
const mapKey: { [key: string]: number } = {
'hello': 3,
};
preferences = {
hello: true,
.....
.....
}
const array = Object.entries(preferences).map(([key, value]) => {
return mapKey[key] && { index: mapKey[key], visible: true };
});
result is:
[undefined, undefined....{ index: mapKey[key], visible: true }]
but i need just [{ index: mapKey[key], visible: true }]
The Array#map method generates an array based on return value, it's not suited for requirement so use Array#reduce method.
const array = Object.entries(preferences).reduce((arr, [key, value]) => {
// push into the array only if defined
columnIndexMap[key] && arr.push({ index: mapKey[key], visible: true });
// return the array reference for next iteration
return arr;
// set initial value as empty array for the result
}, []);
One-liner solution:
const array = Object.entries(preferences).reduce((arr, [key, value]) => (columnIndexMap[key] && arr.push({ index: mapKey[key], visible: true }), arr), []);
The answer with reduce of course works and is efficient. You can also use filter() + map. This has the disadvantage of looking at values twice, but has a readability advantage. Which is more important, of course, depends on your use-case. I would prefer the following unless there is so much data that the additional work is noticeable:
const mapKey = { 'hello': 3, 'test':4, 'test2': 5};
let preferences = { hello: true, test:false, test2:true}
let filtered = Object.entries(preferences)
.filter(([k, visible]) => visible)
.map(([k, visible]) => ({ index: mapKey[k], visible }))
console.log(filtered)

Return boolean true value if object contains true key, not return the value

I'm really stuck on how to return a simple true/false IF my object contains a key with a true value. I do not want to return the key or value itself, just an assertion that it does contain a true value.
E.g
var fruits = { apples: false, oranges: true, bananas: true }
There's a true value in this object. I don't care which one is true... I just want to be able to return true because there is a true value.
My current solution returns ["oranges", "bananas"] not true
Object.keys(fruits).filter(function(key) {
return !!fruits[key]
})
As Giuseppe Leo's answer suggests, you can use Object.values (keys aren't important here) to produce an array of the object's values to call Array#includes on:
const fruits = {apples: false, oranges: true, bananas: true};
console.log(Object.values(fruits).includes(true));
// test the sad path
console.log(Object.values({foo: false, bar: 42}).includes(true));
If Object.keys is permitted but Object.values and includes aren't, you can use something like Array#reduce:
var fruits = {apples: false, oranges: true, bananas: true};
console.log(Object.keys(fruits).reduce((a, e) => a || fruits[e] === true, false));
If you don't have access to anything (or don't like that the reduce approach above doesn't short-circuit), you can always write a function to iterate through the keys to find a particular target value (to keep the function reusable for other targets than true):
function containsValue(obj, target) {
for (var key in obj) {
if (obj[key] === target) {
return true;
}
}
return false;
}
var fruits = {apples: false, oranges: true, bananas: true};
console.log(containsValue(fruits, true));
As you want to know if any of the values is true:
Object.values(fruits).includes(true)
Try with Array.prototype.some():
The some() method tests whether at least one element in the array passes the test implemented by the provided function.
var fruits = { apples: false, oranges: true }
var r = Object.keys(fruits).some(function(key) {
return !!fruits[key]
})
console.log(r);
Though, instead of Object.keys(), it is better to use Object.values() to iterate the objects value directly:
The Object.values() method returns an array of a given object's own enumerable property values, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).
var fruits = { apples: false, oranges: true }
var r = Object.values(fruits).some(f => f)
console.log(r);
You could check with Boolean as callback for Array#some.
const has = o => Object.values(o).some(Boolean);
var a = {},
b = { oranges: true, apples : false },
c = { oranges: false, apples : false };
[a, b, c].forEach(o => console.log(has(o)));

using es6 reduce() to manipulate an object using another object

I have state object as:
this.state = {
letterType: {},
letterTag: {},
departmentName: {},
assignedOfficer: {}
}
and I have an another object sortFilters as:
sortFilters = {
letterType: {
0: "letterType1",
1: "letterType2"
},
letterTag: {},
departmentName: {
0: "dept1"
},
assignedOfficer: {}
}
now what I want is to create a newState object (probably using es6 reduce()) which will be created based on sortFilters object such as:
this.newState = {
letterType: {
letterType1: true,
letterType2: true
},
letterTag: {},
departmentName: {
dept1: true
},
assignedOfficer: {}
}
I think this is possible using es6 reduce() but I am not able to get it to work.
As described, your problem is a use case for Object.assign, since you just want to copy the contents of four objects (on sortFilters) into your state object. You'd either do it manually:
Object.assign(this.newState.letterType, sortFilters.letterType);
Object.assign(this.newState.letterTag, sortFilters.letterTag);
Object.assign(this.newState.departmentName, sortFilters.departmentName);
Object.assign(this.newState.assignedOfficer, sortFilters.assignedOfficer);
...or in a loop:
for (const name of Object.keys(sortFilters)) {
Object.assign(this.newState[name], sortFilters[name]);
}
That merges the entries from the sortFilters objects with the ones in this.newState. If you want to replace them instead, you'd use assignment (and probably a shallow copy, but that depends on how sortFilters is used later):
this.newState.letterType = Object.assign({}, sortFilters.letterType);
this.newState.letterTag = Object.assign({}, sortFilters.letterTag);
this.newState.departmentName = Object.assign({}, sortFilters.departmentName);
this.newState.assignedOfficer = Object.assign({}, sortFilters.assignedOfficer);
or
for (const name of Object.keys(sortFilters)) {
this.newState[name] = Object.assign({}, sortFilters[name]);
}
Note that Object.assign does a shallow copy; if any of these objects are nested, you'll need something else.
As of ES2018, you can use property spread when creating the new objets instead of Object.assign:
for (const name of Object.keys(sortFilters)) {
this.newState[name] = {...sortFilters[name]};
}
There are other methods to achieve the same, as reduce is used over arrays. But if using reduce is a practice, then Yes that is also possible, a rough way could be like the following, where we can use reduce over keys of objects, which is an array.
let state = {letterType: {},letterTag: {},departmentName: {},assignedOfficer: {}}
let sortFilters = {letterType: {0: "letterType1",1: "letterType2"},letterTag: {},departmentName: {0: "dept1"},assignedOfficer: {}}
let newState = Object.keys(state).reduce(function(prev, current) {
let val = sortFilters[current]
if (!val) {
prev[current] = state[current]
} else {
prev[current] = Object.keys(val).reduce(function (p, c) {
p[val[c]] = true
return p
}, {})
}
return prev
}, {})
console.log(newState)
For more details about reduce and Object.keys, please refer to Mozilla Developer Network's documentation.
Not sure what "state" or this.state means in this context...React? Anyhow, tt looks like you just want to simply unpack certain properties and manipulate them. If so destructuring assignment might help. Refer to this article section on Destructuring Nested Objects and this section on Nested object and array destructuring
Demo
let sortFilters = {
letterType: {
0: "letterType1",
1: "letterType2"
},
letterTag: {},
departmentName: {
0: "dept1"
},
assignedOfficer: {}
}
// Making a copy of sortFilters
let final = JSON.parse(JSON.stringify(sortFilters));
// Desruturing final, assigning variables and values
let {
letterType: {
letterType1: letterType1 = true,
letterType2: letterType2 = true
},
letterTag: {},
departmentName: {
dept1: dept1 = true
},
assignedOfficer: {}
} = final;
console.log(letterType1);
console.log(letterType2);
console.log(dept1);

Get object keys based on value

I have a use case where I have an object of varying values, and I need to get all of these keys that have a specific value. For instance, here is a sample object:
myObject = {
Person1: true,
Person2: false,
Person3: true,
Person4: false
};
The key names will vary, but the valid values are true or false. I want to get an array of the names that have a value of true:
myArray2 = [
'Person1',
'Person3
];
I've been trying to use various lodash functions in combination such as _.key() and _.filter, but with no luck. How can I accomplish this? I'm open to pure JS or Lodash options.
UPDATE: I accepted mhodges' answer below as the accepted answer, although others gave me the same answer. Based on that, I came up with a Lodash version:
var myArray = _(myObject).keys().filter(function(e) {
return myObject[e] === true;
}).value();
If I understand your question correctly, you should be able to use basic .filter() for this.
myObject = {
Person1: true,
Person2: false,
Person3: true,
Person4: false
};
var validKeys = Object.keys(myObject).filter(function (key) {
return myObject[key] === true;
});
Since Lodash was tagged: With pickBy the values can be filtered (and the keys obtained with _.keys ):
var myArray2 = _.keys(_.pickBy(myObject));
var myObject = { Person1: true, Person2: false, Person3: true, Person4: false };
var myArray2 = _.keys(_.pickBy(myObject));
console.log(myArray2 );
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Use Object.keys():
var object = {
1: 'a',
2: 'b',
3: 'c'
};
console.log(Object.keys(object));
Alternative solution:
var keys = [];
for (var key in object) {
if (object.hasOwnProperty(key)) {
keys.push(key);
}
}
console.log(keys);
Don't forget to check a key with the help of hasOwnProperty(), otherwise this approach may result in unwanted keys showing up in the result.
You can do this with Object.keys() and filter().
var myObject = {
Person1: true,
Person2: false,
Person3: true,
Person4: false
};
var result = Object.keys(myObject).filter(function(e) {
return myObject[e] === true;
})
console.log(result)
ES6 version with arrow function
var result = Object.keys(myObject).filter(e => myObject[e] === true)

Categories