Filter object of object with RxJS - javascript

I have the following stream and I would like to filter and keep only the params that are true(boolean).
{
"colors": {
"red": "",
"yellow": true,
"green": false
},
"size": {
"t5": true,
"t10": "",
"t20": true
}
}
I need the output to look like this:
{
"colors": ["yellow"],
"size": ["t5,t20"]
}
Here are my thougt when I try to deal with this:
I tried to use map but without success. I guess it's because it's an
object and not an array.
All keys and values are dynamic in this object so I can't use them to
manipulate the object.
flatMap() help me a bit but I don't know what to do with it as I
don't know the name of the keys.
this.form.valueChanges
.debounceTime(400)
.flatMap(x => Object.values(x))
.subscribe(console.log)

This is not an rxjs issue, just a plain js mapping:
getTruthyKeys(obj: any): Array<string> {
return Object.keys(obj).filter(key => obj[key] === true);
}
mainKeysToTruthyLists(obj: any): Array<{key: string, truthy: Array<string>}> {
let result = {};
Object.keys(obj).forEach(key => result[key] = getTruthyKeys(obj[key]);
return result;
}
and now you can apply it as a map on your stream:
this.form.valueChanges
.debounceTime(400)
.map(mainKeysToTruthyLists)
.subscribe(console.log)

This isn't really a RxJS question. All that RxJS should do here is map. This can be done with Object.entries:
this.form.valueChanges
.debounceTime(400)
.map(obj => {
return Object.entries(obj)
.reduce((newObj, [key, subObj]) => {
const subArr = Object.entries(subObj)
.filter(([, subValue]) => subValue)
.map(([subKey]) => subKey);
return Object.assign(newObj, { [key]: subArr });
}, {})
})

Related

How to include data to object if element is not falsy?

I have the map:
map(([reportProperties, reportObjectsProperties, textProperties, visibleText]) => {
return { reportObjectsProperties, reportProperties, textProperties, visibleText };
}),
I try to check if all parameters are not falsy include them to result object like this:
map(([reportProperties, reportObjectsProperties, textProperties, visibleText]) => {
if(visibleText)
return { reportObjectsProperties, reportProperties, textProperties, visibleText };
if(reportObjectsProperties && reportObjectsProperties.length)
return { reportObjectsProperties, reportProperties, textProperties, visibleText };
....
}),
How to make this more elegant?
My suggestion is to look into Array.prototype.filter() method paired with
Object.entries to filter out the falsy values. And pair it with a reduce function to reconstruct the new object
I would take this into the following direction:
const arr = [{a: true, b: true, c: false}, {a: false, b: true}]
const excludeKeys = ["c"]
let result = arr.map(obj => {
return Object.entries(obj).filter(([key, value]) => !excludeKeys.includes(key) && value)
.reduce((prev, [key, value]) => ({...prev, [key]: value}), {})
})
This provides a functional and pretty generic interface for filtering out the falsy / excluded object key / values.
Assuming your object is an array, you can check if all values exists using every method. And you don't need map there because you don't do a transformation on your object.
instead of
.map(arr => {
... return arr;
});
do
.filter(row => row.every(cell => typeof cell !== undefined && cell !== null));

Adding elements in the array on the basis of an object

const prefer={"cs":{"c1":true,"c2":true,"c3":false,"c4":true}}
setArray((prevArray)=>{
return prevArray.filter(function(array){
//code here
})
});
I want to set array on the basis of prefer object, like if c1 is true then is want to add it in the array
Do I need to initialize the array too?
In above example array=["c1","c2","c4"] .
Also do we need filter method or can we use some other method
You can use for-in loop to iterate map/object
const prefer = { c1: true, c2: true, c3: false, c4: true };
let arr = [];
for (let key in prefer) {
if (prefer[key]) arr.push(key); // modify how u want to save
// arr.push({ [key]: prefer[key] }); // if u want key and value
}
console.log(arr);
You can express it as follows,
const prefer = { "c1":true, "c2":true, "c3":false, "c4":true }
const output = Object.entries(prefer)
.filter(([, value]) => value) // !!value or Boolean(value) to coerce
.map(([key]) => key)
console.log(output)
This is sort of convoluted, but it works:
const prefer = {
"c1": true,
"c2": true,
"c3": false,
"c4": true
}
var setArray = ( prevArray ) => {
return Object.keys(prevArray).filter(function(val, i) {
if ( prevArray[Object.keys(prevArray)[i]] ) return Object.keys(prevArray)[i];
})
};
console.log( setArray(prefer) );

how to combine the all values of keys which is inside an object to a new array in es6

i have the dynamic data coming in this format, i want to turn into and array based on the toolset object keys, but toolset object have not constant number of keys
{ toolset:{
info1:{
event-date:{},
event-time:{},
},
info2:{
event-location:{},
event-url:{},
}
}}
i want it like this
inputs=[{event-date:{}},{event-time:{}},{event-location:{}},{event-url:{}}]
with the minimal code in javascript es6
You could use map, flat and Object.entries methods to return flat array of objects.
const data = {
toolset: {
info1: {
'event-date': {},
'event-time': {},
},
info2: {
'event-location': {},
'event-url': {},
}
},
}
const inputs = Object
.values(data.toolset)
.map(Object.entries)
.flat()
.map(([k, v]) => ({[k]: v}))
console.log(inputs)
This should work:
let obj = {
toolset: {
info1: {
"event-date": {},
"event-time": {}
},
info2: {
"event-location": {},
"event-url": {}
}
}
};
let finalArray = [];
Object.values(obj.toolset).forEach(obj => {
Object.entries(obj).forEach(([key, val]) => finalArray.push({ [key]: val }));
});
console.log(finalArray); // <== your desired array

multiselect filter array with nested object

I use vue-multiselect to let user select items with filtering options:
query = "what user type in input field"
allLinks.filter(l=>l.labelName.includes(query))
and it works, but now I would like to extend the filtering to all properties of my object with this structure :
{
"labelName":"LK000056",
"extPort":{
"aPort":"EXTA-EQ001/board10port02",
"zPort":"EXTZ-EQ012/board09port02"
}
}
I would like with one query to get parent object if query match on labelName, aPort or zPort.
it is possible ? or maybe with a conditional way like :
allLinks.filter(l=>if(!l.labelName.includes(query)){l.extport.aPort.includes(query)}else{l.extport.zPort.includes(query)})
thank for helping me
forgive my approximate English I am French
You could recursively flatten the objects to array of strings, then search in them via Array.filter, Array.some & Array.includes:
const data = [{ "labelName":"LK000056", "extPort":{ "aPort":"EXTA-EQ001/board10port02", "zPort":"EXTZ-EQ012/board09port02" } }, { "labelName":"LK000057", "extPort":{ "aPort":"EXTA-EQ001/board123", "zPort":"EXTZ-EQ012/board333" } }]
const flatten = (obj, a=[]) => Object.values(obj)
.reduce((r,c) => (typeof c == 'object' ? flatten(c,a) : r.push(c), r), a)
const search = (d, t) =>
d.filter(x => flatten(x).some(x => x.toLowerCase().includes(t.toLowerCase())))
console.log(search(data, 'board123'))
console.log(search(data, 'LK000056'))
console.log(search(data, 'EXTZ-EQ012'))
Note that this is a generic approach and will work regardless of the nested levels of data, for example:
const data = [{
"A": {
"B": {
"C": {
"data": '11'
},
}
}
}, {
"D": {
"E": {
"data": '22'
}
}
}, {
"F": "33"
}]
const flatten = (obj, a = []) => Object.values(obj)
.reduce((r, c) => (typeof c == 'object' ? flatten(c, a) : r.push(c), r), a)
const search = (d, t) => d.filter(x =>
flatten(x).some(x => x.toLowerCase().includes(t.toLowerCase())))
console.log(search(data, '22'))
console.log(search(data, '11'))
console.log(search(data, '33'))
You can use Object.values to get the values of the object as an array, then use some to test if one or more items match.
const items = [{
"labelName": "LK000056",
"extPort": {
"aPort": "EXTA-EQ001/board10port02",
"zPort": "EXTZ-EQ012/board09port02"
}
}, {
"labelName": "234234",
"extPort": {
"aPort": "abc123",
"zPort": "1234567890"
}
}]
function search(query, data) {
return data.filter(i => {
if (i.labelName.includes(query)) return true
if (Object.values(i.extPort).some(v => v.includes(query))) return true
return false
})
}
console.log(search("EQ001", items))
console.log(search("1234567890", items))
console.log(search("lalalala", items))
if it can help I think found a functional solution let me know if something better can be done ...
allLinks.filter(l=>{
if (l.sdhPort.zSDHPort!=undefined && l.extPort.aPort.toUpperCase().includes(query.toUpperCase())){return true}
if (l.extport.zPort!=undefined && l.extPort.zPort.toUpperCase().includes(query.toUpperCase())){return true}
if (l.labelName!=undefined && l.labelName.toUpperCase().includes(query.toUpperCase())){return true}
})
wow thanks both of you !
it seems really interesting this technic ... but let me some time to understand it
what do think about what I found ?
best regards

Grouping a data set by the key of an object and producing an array

I have a set of data which I have grouped by the key of the object - For each key of the object I need to produce an array of the id's . So instead of an array of object it is just a flat array of the id's.
I have managed to get the data back but can't seem to get just the id's out.
Here is the data set
let result = {
"age_group_ids": [
{
"id": 5
},
{
"id": 4
},
{
"id": 3
},
{
"id": 9
}
],
"family_group_ids": [
{
"id": 2
},
{
"id": 1
}
],
"earnings_group_ids": [
{
"id": 7
},
{
"id": 6
}
]
}
This is the function I have written but it does not seem to work.
Object.keys(result).forEach(key => {
return result[key].map(item => {
item.id
})
})
The end result I want an object like this
{"age_group_ids": [5,4, 3, 9],
"family_group_ids": [2, 1],
"earnings_group_ids": [7,6]
Instead of returning from the forEach, assign the results back the result[key]. Removed the curly brackets arround the item.id of the map, so value would be added to the array.
const result = {"age_group_ids":[{"id":5},{"id":4},{"id":3},{"id":9}],"family_group_ids":[{"id":2},{"id":1}],"earnings_group_ids":[{"id":7},{"id":6}]};
Object.keys(result).forEach(key => {
result[key] = result[key].map(item => item.id)
})
console.log(result);
let result = {"age_group_ids":[{"id":5},{"id":4},{"id":3},{"id":9}],"family_group_ids":[{"id":2},{"id":1}],"earnings_group_ids":[{"id":7},{"id":6}]};
function groupById(groups) {
return Object.keys(groups).reduce((result, key) => {
result[key] = result[key] || [];
result[key] = groups[key].map(o => o.id);
return result;
}, {});
}
console.log(groupById(result))
Use Array.prototype.reduce() instead of Array.prototype.forEach(), and Object.entries() for convenience so you don't have to use a scoped reference to result[key]. You can also use object destructuring to reference key, values, and id in a more concise way. Don't forget that you need to initialize acc to an empty object for the reduce() callback:
const result = {age_group_ids:[{id:5},{id:4},{id:3},{id:9}],family_group_ids:[{id:2},{id:1}],earnings_group_ids:[{id:7},{id:6}]}
const output = Object.entries(result).reduce((acc, [key, values]) => Object.assign(acc, {
[key]: values.map(({ id }) => id)
}), {})
console.log(output)

Categories

Resources