Filtering Observable with Rxjs - javascript

I'm trying to get a list of active jobs for the current user:
jobListRef$: Observable<Job[]>;
...
this.afAuth.authState.take(1).subscribe(data => {
if (data && data.uid) {
this.jobListRef$ = this.database.list<Job>('job-list', query => {
return query.orderByChild("state").equalTo("active");
})
.snapshotChanges().map(jobs => {
return jobs.map(job => {
const $key = job.payload.key;
const data = { $key, ...job.payload.val() };
return data as Job;
});
})
.filter(val =>
val.map(job => {
return (job.employer == data.uid || job.employee == data.uid);
})
);
}
});
The problem begins only at the filtering. According to the official documentation the right part of its argument should return boolean, so like in my case. But still it returns me whole entries without filtering them.

You're returning the result of the Array.map call, see its return value: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
It looks like you maybe want to use map instead of filter:
.snapshotChanges().map(...)
.map(val => val.map(job => {
return (job.employer == data.uid || job.employee == data.uid);
}));

I believe you want to reverse the map and filter operators.
.map((jobs: Job[]) =>
jobs.filter((job: Job) => job.employer === data.uid || job.employee === data.uid )
);
(map to transform one array into another, filter to reduce the array).
Or you can chain filter on to the map that performs the type-conversion,
.map(jobs => {
return jobs.map(job => {
const $key = job.payload.key;
const data = { $key, ...job.payload.val() };
return data as Job;
})
.filter(job => job.employer === data.uid || job.employee === data.uid )
})

Not sure to understand the whole problem, but it might be something like:
this.jobListRef$ = this.afAuth.authState
.filter(data => !!data && !!data.uid)
.take(1)
.switchMap(data =>
this.database.list<Job>('job-list', query => query.orderByChild("state").equalTo("active"))
.snapshotChanges()
.map(jobs =>
jobs.map(job => {
const $key = job.payload.key;
const data = { $key, ...job.payload.val() };
return data as Job;
})
)
.map((jobs: Job[]) =>
jobs.filter(job => (job.employer == data.uid || job.employee == data.uid))
)
);

Related

How to select api response with a condition angular javascript?

I have an api response that gets me a multiple objects and inside that object I have an array called "employes" ,"employes" has an item called "avis" .
I want to select the object when the value inside of employes>recrutments>avis="accepter" .
all the array inside the object have to get the value "avis"="accepter" to select that object.
If one of the employes has a diffrent value of "avis" then the object is decline.
I tried to map on the response and I used every method of JavaScript
getJuction():void{
this.getAlloff.getjuction()
.subscribe(data => {
console.log(data);
this.test = data.map(function(element:any){
return element.employes.map((employe:any)=>employe.recrutements.avis === 'accepter').every((emel:any) => emel === true ) ;
});
console.log(this.test)
var test2 = data.filter((employe:any)=>employe);
console.log(test2)
// var test =data.flat();
// console.log(test)
}, error => console.log(error));
}
This is the result I get.
The only object that has "accepter" for every value gets true
I found the solution, I just changed the method from "map" to "filter" method.
This was the previous code:
getJuction():void{
this.getAlloff.getjuction()
.subscribe(data => {
this.test = data.map(function(element:any){
return element.employes.map((employe:any)=>employe.recrutements.avis === 'accepter').every((emel:any) => emel === true ) ;
});
}, error => console.log(error));
}
This is the new code:
getJuction():void{
this.getAlloff.getjuction()
.subscribe(data => {
this.test = data.filter(function(element:any){
return element.employes.map((employe:any)=>employe.recrutements.avis === 'accepter').every((emel:any) => emel === true ) ;
});
}, error => console.log(error));
}
this.getAlloff.getjuction()
.subscribe(data => {
//this.result some property to store value
this.result = data.map( (item : any) => {
return item.employees.filter((item : any)=> {
return item.recrutements.alves == 'accepter'
})
})
}
try this out if you getting Multiple Object in data

Attempting to map through an array of data and push items to different arrays

I'm building a function at the moment which receives data from an API call, maps through it (it's an array of objects), and pushes an item to a respective array depending on one of its values.
However, it only seems to be pushing one item into each array and no more.
There are no errors in my code, so not sure what I'm doing wrong - I assume I'm overwriting the array each time but not sure.
Here's the state arrays which the items need to be pushed into:
const [backlog, setBacklog] = useState([])
const [inProgress, setInProgress] = useState([])
const [paused, setPaused] = useState([])
const [completed, setCompleted] = useState([])
And here is the function itself:
const fetchData = async () => {
await axios.get("/api/fetch/fetchData")
.then(async (response) => {
const data = await response.data;
setAllTasks(data)
data.map(item => {
if (item.status === 'backlog') {
setBacklog([...backlog, item])
} else if (item.status === 'in-progress') {
console.log(item)
setInProgress([...inProgress, item])
} else if (item.status === 'paused') {
setPaused([...paused, item])
} else if (item.status === 'completed') {
setCompleted([...completed, item])
}
})
})
}
Because set functions are batched it's likely that the variable has not yet been updated.
To modify your program (as is) use function state updating:
const fetchData = async () => {
await axios.get("/api/fetch/fetchData")
.then(async (response) => {
const data = await response.data;
setAllTasks(data)
data.map(item => {
if (item.status === 'backlog') {
setBacklog(prevBackLog => [...prevBackLog, item])
} else if (item.status === 'in-progress') {
console.log(item)
setInProgress(prevInProgress => [...prevInProgress, item])
} else if (item.status === 'paused') {
setPaused(prevPaused => [...prevPaused, item])
} else if (item.status === 'completed') {
setCompleted(prevSetCompleted => [...prevSetCompleted, item])
}
})
})
}
Note: it would be much better to come up with a way to use a single set function to update all of these values in one state update.
Maybe with the currently available set functions building a complete state and doing one update per status doing something like:
function groupBy(arr, property) {
return arr.reduce(function (memo, x) {
if (!memo[x[property]]) {
memo[x[property]] = [];
}
memo[x[property]].push(x);
return memo;
}, {});
}
data = groupBy(data, 'status')
setBacklog(prevBackLog => [...prevBackLog, ...data['backlog']])
setInProgress(prevInProgress => [...prevInProgress, ...data['in-progress']])
setPaused(prevPaused => [...prevPaused, ...data['paused']])
setCompleted(prevSetCompleted => [...prevSetCompleted, ...data['completed']])
I think your axios call is considerably more complicated than it needs to be. All those extra async/await & then calls may be your issue. Here's a much simpler solution:
const fetchData = async () => {
const response = await axios.get("/api/fetch/fetchData");
const data = response.data;
data.map(item => {
if (item.status === 'backlog') {
setBacklog(prevBackLog => [...prevBackLog, item])
} else if (item.status === 'in-progress') {
console.log(item)
setInProgress(prevInProgress => [...prevInProgress, item])
} else if (item.status === 'paused') {
setPaused(prevPaused => [...prevPaused, item])
} else if (item.status === 'completed') {
setCompleted(prevSetCompleted => [...prevSetCompleted, item])
}
})
}

Remove duplicates within the output of a reduce function

I am outputting the matched value of projects when the name of my logo matches the items object within projects with the help of a reduce function. However, whenever I click on multiple logos that both match project.items I am rendering duplicates.
Here is my code:
logos.reduce((acc, logo) => {
if (logo.active) {
Object.values(projects).forEach((proj) => {
if (Object.values(proj.items).includes(logo.name)) {
console.log(acc)
acc.push((<Project title={proj.title} routeName={proj.routeName} items={proj.items} description={proj.description}/>));
}
});
}
return acc
}, [])
My first idea was to create another array, run a for loop and iterate through the values like: filteredValues[i].props.title and push the contents of that loop to an array. I ran run a reduce on that array like this but I was not able to eliminate the duplicate:
const filteredArr = arr.reduce((acc, current) => {
const x = acc.find(item => item.title === current.title);
if (!x) {
return acc.concat([current]);
} else {
return acc;
}
}, []);
Anyway, here's the output of acc which I am using to render my Project component
May be below code is what you need.
const filteredArr = this.getUnique(arr, 'title');
getUnique(arr, comp) {
const unique = arr.map(e => e[comp]).map((e, i, final) => final.indexOf(e) === i && i).filter((e) => arr[e]).map(e => arr[e]);
return unique;
}
Steps involve is to:
Store the comparison values in array "arr.map(e => e[comp])"
Store the indexes of the unique objects ".....).map((e, i, final) => final.indexOf(e) === i && i)"
Eliminate the false indexes & return unique objects ".....).filter((e) => arr[e]).map(e => arr[e])"
You can write your original loop like this
logos
.reduce((acc, logo) => {
if (logo.active) {
Object.values(projects).forEach((proj) => {
if (
Object.values(proj.items).includes(logo.name) &&
!acc.find((item) => item.value === logo.name)
) {
console.log(acc);
acc.push({
value: logo.name,
component: (
<Project
title={proj.title}
routeName={proj.routeName}
items={proj.items}
description={proj.description}
/>
),
});
}
});
}
return acc;
}, [])
.map((values) => values.component);
You can use the Map object to filter out duplicates.
let arrFiltered = [];
// form map of unique arr items
const arrMap = new Map();
arr.forEach(item => arrMap.set(item.title, item));
// form array of all items in map
arrMap.forEach(item => arrFiltered.push(item));
I had to run a reduce function outside of the original forEach loop and check those values with a some function.
logos.reduce((acc, logo) => {
if (logo.active) {
Object.values(projects).forEach((proj) => {
if (Object.values(proj.items).includes(logo.name)) {
console.log(acc)
acc.push((<Project title={proj.title} routeName={proj.routeName} items={proj.items} description={proj.description}/>));
}
});
acc = acc.reduce(function (p, c) {
if (!p.some(function (el) { return el.props.title === c.props.title; })) p.push(c);
return p;
}, []);
}
return acc
}, [])

implement infinite scroll with rxjs

I'm having trouble when implement infinite scroll with rxjs
I have tried following snippet
var lastid = null
fromEvent(window, 'scroll')
.pipe(
filter(() => isAtPageBottom()),
exhaustMap(() => from(this.getList(lastid))),
takeWhile(list => list.length !== 0),
scan((cur, list) => [...cur, ...list], [])
)
.subscribe(list => {
this.setState({list: list})
})
async function getList (lastid) {
const list = await request('/api/list?lastid=' + lastid)
listid = list[list.length-1].id
return list
}
How to pass the lastid to each request without the global variable 'lastid'?
Thanks
haven't test the code but you can try the below code snippet. The technique here is to start off with a state that contains your list and lastId then use expand to keep the stream going and listen for scroll
of({ list: [], lastId: null }).pipe(
expand((obj) => {
return fromEvent(window, 'scroll')
.pipe(
filter(() => isAtPageBottom()),
exhaustMap(() => from(this.getList(obj.lastid))),
map(res => ({ list: obj.list.concat(res), lastId: res[res.length - 1].id }))
)
}),
takeWhile(({ list }) => list.length !== 0))

How to return additional values from flatMap in RxJs

I need to return an additional value other than Observable, key string from flatMap from below setup:
this.subscribeInit = this.tradingService.getInProgress()
.pipe(flatMap((s) => {
this.keys = s.map((tdi) => tdi.key);
return this.keys;
}),
flatMap((key) =>{
var res = this.tradingService.getTradingData(key);//returns `Observable<any>`
return res;
}))
.subscribe(res => {
console.log('Key is : ' + res.key);
},
undefined,
() => this.onComplete());
Something like:
flatMap((key) =>{
var res = this.tradingService.getTradingData(key);
return {res,key};
}))
How to achieve this?
Just use map ...
flatMap((key) =>{
return this.tradingService.getTradingData(key).pipe(map((res) => {
return {res,key};
}));
}))
ps: i am a bit confused: isnt flatMap deprecated and replaced my mergeMap, switchMap, ... ?
edit: apparently the problem was caused by using flatMap instead of mergeMap.
You can destructur object as function parameters
this.subscribeInit = this.tradingService.getInProgress()
.pipe(flatMap((s) => {
this.keys = s.map((tdi) => tdi.key);
return this.keys;
}),
flatMap((key) =>{
var res = this.tradingService.getTradingData(key);
return {res,key}; // pack res and key into object
}))
.subscribe(({res,key}) => { // destructuring previously returned object
console.log('Key is : ' + res[key]);
},
undefined,
() => this.onComplete());
const val1 = 'some val1', val2 = 'some val2';
Promise.resolve({val1, val2}).then(({val1, val2}) => {
console.log(val1);
console.log(val2);
});

Categories