How to select api response with a condition angular javascript? - 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

Related

Why first element of array is empty?

I have a function which returns an Observable and once its fires I'm looping the subscribed array
this.productService.getProductBySeo(proudctName).pipe(
catchError(e => of(null)),
this.unsubsribeOnDestroy
).subscribe(res => {
if (res) {
this.categoriesService.currentCategory.subscribe(menus => {
const activeElem = res;
const allItems = menus;
allItems.map(item => {
console.log(item)
console.log(item.id) // can see id of each elem
console.log(item.children) // first element of item.children is empty
....
UPD 1
I have changed to switchMap as it was advised in comments , anyway the arrays is empty
this.productService.getProductBySeo(proudctName).pipe(
catchError(e => of(null)),
switchMap(res => {
this.categoriesService.currentCategory.subscribe(menus => {
if (menus) {
const activeElem = res;
const allItems = menus;
allItems.map(item => {
console.log(item)
// console.log(item.id) // can see id ,
console.log(item.children) // still empty :-(
item.children.map(item => {
// console.log(item);
return item
})
const fountObj = item.children.find(el => {
// console.log('element ', el)
return el === activeElem;
});
if (fountObj) {
this.totalStyle = item.seo_url; // to identify total style of the component
}
})
}
// console.log(menus)
})
return of(res)
}),
this.unsubsribeOnDestroy
).subscribe(res => {
if (res) {
this.product = res;
Regarding what do I expect to see is when subscription fires in this.categoriesService.currentCategory.subscribe, I want to see item.children in console. As I see only empty array when log item.children , but when I console just item, I can see that item.children has 2 items in own Array
Not a great solution, But I suspect that it might have been changed by reference on somewhere else. Try deep cloning and looping.
i.e.
const allItems = JSON.parse(JSON.stringify(menus));
allItems.forEach(item => {
console.log(item)
console.log(item.children)
Also, forEach looks much better than a map in such circumstances.

Processing an Object During Fetch Returns 'undefined'

I'm fetching an API of dogs, it returns an object.
I want to make changes in the object before I convert
it to an array and setState() it.
Problem is it returns 'undefined'.
I assume the function is ok because it does work if I run in outside the fetch, or maybe I'm missing something here?
componentDidMount() {
fetch("https://dog.ceo/api/breeds/list/all")
(res => res.json())
.then(res => res.message)
.then(res => this.processReq(res)) // <--- undefined
.then(res => Object.entries(res))
.then(res => this.setState({ allBreeds: res }));
}
processReq = breed =>
Object.keys(breed).forEach(key => {
if (key === "australian") {
breed["shepherd"] = [key];
delete breed[key];
} else if (key === "germanshepherd") {
breed["shepherd"].push("german");
delete breed[key];
}
});
Is there a way to manipulate the object during the fetch?
Or maybe it would be easier to setState() it as an array and then process it?
Actually forEach function doesn't return anything so that's why it's giving undefined, you should change it to:
processReq = breed => {
Object.keys(breed).forEach(key => {
if (key === "australian") {
breed["shepherd"] = [key];
delete breed[key];
} else if (key === "germanshepherd") {
breed["shepherd"].push("german");
delete breed[key];
}
});
return breed;
}
The processReq function doesn't have a return statement, so it just returns undefined. Just put a return breed; on the end of it and you'll be fine.

Is there a way to use a observable returning function for each element of another observable array?

I get an Observable<Group[]> from my Firebase collection.
In this Group class is an id which I wanna use to retrieve another dataset array from Firebase, which would be messages for each unique group Observable<Message[]>.(each group has its own chat: Message[])
And it want to return an observable which hold an array of a new Type:
return { ...group, messages: Message[] } as GroupWithMessages
the final goal should be Observable<GroupWithMessages[]>
getGroupWithChat(): Observable<GroupWithMessages[]> {
const groupColl = this.getGroups(); // Observable<Group[]>
const messages = groupColl.pipe(
map(groups => {
return groups.map(meet => {
const messages = this.getMessagesFor(group.uid);
return { messages:messages, ...group} as GroupWithMessages
});
})
);
return messages;
}
}
and here the Message function
getMessagesFor(id: string): Observable<Message[]> {
return this.afs.collection<Message>(`meets/${id} /messages`).valueChanges();
}
sadly that doesnt work because when i create the new Obj I cannot bind messages:messages because messages ist vom typ Observable<Message[]>
I hope that cleares things
UPDATE:
my main problem now comes down to this:
getGroupsWithMessages() {
this.getJoinedGroups()
.pipe(
mergeMap(groups =>
from(groups).pipe(
mergeMap(group => {
return this.getMessagesFor(group.uid).pipe(
map(messages => {
return { ...group, messages } as GroupIdMess;
})
);
}),
tap(x => console.log('reaching here: ', x)),
toArray(),
tap(x => console.log('not reaching here = completed: ', x))
)
),
tap(x => console.log('not reaching here: ', x))
)
.subscribe(x => console.log('not reaching here: ', x));
}
when i call that function my console.log is as follows:
Not sure if I follow what you're doing here but the logic look like you'd want:
getGroupWithChat() {
return this.getGroups.pipe(map(groups=> {
return groups.map(group => this.getMessagesFor(group.uid));
})).subscribe(); // trigger "hot" observable
}
Let me know if I can help further after you clarify.
UPDATE:
So it looks like you need to get the UID of the group before making the call to get the GroupMessages[]?
get Group: Observable
call getMessagesFor(Group.uid)
this example gets groups result$ then
concatMap uses groups result$ to make the messages query
this.getGroups().pipe(
concatMap((group: Group) => this.getMessagesFor(group.uid))
).subscribe((messages: GroupWithMessages[]) => {
console.log(messages);
});
You may still want to map them together but it seems like you know how to do that. concatMap waits for the first to finish, then makes the second call which you need.
Is this closer?
Use forkJoin to wait for messages to be received for all groups. Then map the result of forkJoin to an array of GroupWithMessages like this -
getGroupWithChat(): Observable<GroupWithMessages[]> {
return this.getGroups()
.pipe(
switchMap(groups => {
const messagesForAllGroups$ = groups.map(group => this.getMessagesFor(group.uid));
return forkJoin(messagesForAllGroups$)
.pipe(
map(joined => {
//joined has response like -
//[messagesArrayForGroup0, messagesArrayForGroup1, messagesArrayForGroup2....];
const messagesByGroup = Array<GroupWithMessages>();
groups.forEach((group, index) => {
//assuming that GroupWithMessages has group and messages properties.
const gm = new GroupWithMessages();
gm.group = group;
gm.messages = joined[index];
messagesByGroup.push(gm);
});
return messagesByGroup;
})
)
})
)
}
I usually do that by splitting Observable<any[]> to Observable<any> and then mergeMap the results to inner Observable.
Something like this should work:
getMessagesFor(id: string): Observable<number> {
return of(1);
}
getGroups(): Observable<string[]> {
return of(["1", "2"]);
}
getGroupWithChat() {
this.getGroups().pipe(
mergeMap(groups => from(groups)), // Split the stream into individual group elements instead of an array
mergeMap(group => {
return this.getMessagesFor(group).pipe(
map(messages => {
return Object.assign(group, messages);
})
);
})
);
}
Edit:
Consider BehaviorSubject. It doesn't complete at all:
const behSub: BehaviorSubject<number[]> = new BehaviorSubject([1, 2, 3]);
setTimeout(() => {
behSub.next([4, 5, 6]);
}, 5000);
behSub
.pipe(
mergeMap(arr =>
from(arr).pipe(
tap(), // Do something with individual items, like mergeMap to messages
toArray() // Go back to array
)
)
)
.subscribe(console.log, null, () => {
console.log('Complete');
});

Filtering Observable with Rxjs

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))
)
);

JavaScript - ES6 function does not work for some weird reason

I am setting the below function in order to retrieve it after.
However, for some reason, it does not work:
constructor(private nativeStorage: NativeStorage) {
// I am calling it this way:
this.userId = 123;
this.getItem("userId", this.userId);
// If I replace the shortcut by the below it works fine
/* this.nativeStorage.getItem("userId).then(
data => this.userId = data,
error => console.error(error)
); */
}
getItem(itemKey, itemValue) {
return this.nativeStorage.getItem(itemKey).then(
data => itemValue = data,
error => console.error(error)
);
}
I believe that I am missing something here, that's why it doesn't work
You're assigning data to itemValue which is just a copy of this.userId. JS doesn't support pass by reference that could make that possible. Instead you can just use itemKey to assign to the class instance directly like this:
getItem(itemKey) {
return this.nativeStorage.getItem(itemKey).then(
data => this[itemKey] = data, // assign data to this[itemKey] which will be this.userId (in the example above)
error => console.error(error)
);
}
JavaScript is not supporting call by reference. It's only by value arg passing. So values can be updated only by object references only.
In reference to the part/comments where you can't have an async constructor and therefore I extract all the async parts into a factory.
I have no experience yet with ionic, so you should take the following as pseudocode:
import { NativeStorage } from '#ionic-native/native-storage';
function constructor(public userId: String){ }
//and the factory function
//don't know how exactly how to export stuff in that framework
function createById(userId){
return NativeStorage.getItem('userId')
.then(
userId => new YourClass(userId)
error => console.error(error)
)
}
or in the case where you want to assign multiple properties:
//a utility to resolve with { key: Promise<value> } mappings + pretty much everything you throw at it.
//warning: doesn't resolve promises in nested structures (by design),
//stuff like {key: { foo: Promise<value> }}
//ideally you'd put that in a seperate module since it can be handy in many places.
function resolve(obj){
if(Array.isArray(obj)
return Promise.all(obj);
if(typeof obj === "function")
return Promise.resolve().then(obj);
if(!obj || typeof obj !== "object" || "then" in obj && typeof obj.then === "function")
return Promise.resolve(obj);
var keys = Object.keys(obj);
return Promise.all( keys.map(k => obj[k]) )
.then(values => combine(keys, values));
}
//takes two Arrays, a matching set of keys and values and combines that into an object.
function combine(keys, values){
return keys.reduce((acc, key, index) => {
acc[key] = values[index];
return acc;
}, {});
}
const assignTo = target => source => Object.assign(target, source);
//in this case we use assign all async values outside of the constructor
function constructor(){
this.userId = null;
this.userName = null;
}
function createById(userId){
return resolve({
userId: NativeStorage.getItem('userId'),
userName: NativeStorage.getItem('userName'),
//...
}).then(
assignTo( new YourClass() ),
error => console.error(error)
)
}
Or if this is still to much repetition for you:
//a utility to fetch multiple items at once
function getItems(...keys){
return Promise.all( keys.map( key => NativeStorage.getItem(key) ) )
.then(values => combine(keys, values));
}
//and a quick test
getItems("userId", "userName", ...).then(console.log);
//or in the context of the last snippet:
getItems("userId", "userName", ...).then(assignTo( new YourClass() ));
Hope this helps shows you a different approach to your problems.

Categories