I’ve got an Observable that has an array of objects and I would like to convert them to a different object using a second observable. This is part of a larger project so to simplify this for my question I am going to have an observable that has an array of number and I would like to convert them to a string. I started with the following.
const response$ = of({results: [1, 2, 3]});
response$.pipe(
map((response) => {
return response.results.map((id) => {
return id.toString();
})
})
)
.subscribe((response: string[]) => {
console.log(response);
})
The response in the subscribe will be an array of string as expected. Now I need to use a second observable to convert the number to string (again just to make the question simpler). So I replaced return id.toString() with return of(id.toString()) to simulate making a second call to an observable.
const response$ = of({results: [1, 2, 3]});
response$.pipe(
map((response) => {
return response.results.map((id) => {
return of(id.toString());
})
}),
)
.subscribe((response: Observable<string>[]) => {
})
Now the signature of the response is Observable<string>[] but I need the response to be string[] so I started reading about other RxJS operators and I ended up with the following.
const response$ = of({results: [1, 2, 3]});
response$.pipe(
concatMap((response) => {
return response.results.map((id) => {
return of(id.toString());
})
}),
concatAll()
)
.subscribe((response: string) => {
console.log('bar', response);
})
I used concatMap() and concatAll() because I need the call to the second observable to happen in sequence. The problem now is that my response is a string and I get three calls to the subscriber “1” “2” “3”. I need one response of string[]. Can someone explain how to take an Observable<string>[] and convert it to Observable<string[]> in my example?
I think what you're looking for is this:
const response$ = of({results: [1, 2, 3]});
response$.pipe(
switchMap((response) => {
// map array to observables and execute all with forkJoin
return forkJoin(...response.results.map((id) => {
return of(id.toString());
}))
})
)
.subscribe((response: string) => {
console.log('bar', response);
})
however, this is going to execute in parallel. if you need sequential execution on the inner, you can use concat and reduce
const response$ = of({results: [1, 2, 3]});
response$.pipe(
switchMap((response) => {
// map array to observables and execute all with concat and collect results with reduce
return concat(...response.results.map((id) => {
return of(id.toString());
})).pipe(reduce((acc, v) => acc.concat([v]), []))
})
)
.subscribe((response: string) => {
console.log('bar', response);
})
only thing to be careful of is to make sure response.results has items in it. may need a length check like:
if (!response.results.length)
return of([])
Related
I am streaming api. With the API, I get 1 item each and add to the list. The fact is that the api stream works in a circle, and duplicates are added to the list. How can I eliminate duplicates?
Code add list:
groupData.map((dynamic item) => GetOrder.fromJson(item))
.where((element) {
if (element.orderId != null) {
if (!list.contains(element)) {
list.add(element);
}
return true;
} else {
return false;
}
}).toList();
If elements are primitives, you can use a Set:
final myList = ['a', 'b', 'a'];
Set.from(myList).toList(); // == ['a', 'b']
but if elements are objects, a Set wouldn't work because every object is different from the others (unless you implement == and hashCode, but that goes beyond this answer)
class TestClass {
final String id;
TestClass(this.id);
}
...
final myClassList = [TestClass('a'), TestClass('b'), TestClass('a')];
Set.from(myClassList).toList(); // doesn't work! All classes are different
you should filter them, for example creating a map and getting its values:
class TestClass {
final String id;
TestClass(this.id);
}
...
final myClassList = [TestClass('a'), TestClass('b'), TestClass('a')];
final filteredClassList = myClassList
.fold<Map<String, TestClass>>({}, (map, c) {
map.putIfAbsent(c.id, () => c);
return map;
})
.values
.toList();
That said, this should work for you
groupData
.map((dynamic item) => GetOrder.fromJson(item))
.fold<Map<String, GetOrder>>({}, (map, element) {
map.putIfAbsent(element.orderId, () => element);
return map;
})
.values
.toList();
You can use Set instead
A Set is an unordered List without duplicates
If this is not working, then chances are that u have different object for the same actual object. (meaning, you have in 2 different places in memory)
In this case .contains or Set will not work
I have a function like this
export const fetchChildrenNews = createAsyncThunk('news/fetch1', async ([item, news]) => {
const res = await Promise.all(item.kids.map(id => {
let url = `https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`;
return fetch(url);
}));
const jsons = await Promise.all(res.map(r => r.json()));
let users = {...item, kids: jsons};
item.kids = []//doesn't work
item.id = 0 //doesn't work
//I want to find a branch in the original tree and replace it
const tree = (obj) => {
for (let key in obj) {
if (key === "id" && obj[key] === users.id) {
obj = users;
}
if (key == "kids") {
tree(obj);
}
}
}
tree(item);
where item is a nested object record: {by: 'nullzzz', descendants: 47, id: 28808556, kids: Array(13), score: 117}. kids property contains array of ids and in the users variable it becomes an array of records. and my goal change record.kids = [0, 7, 14] to record.kids = users ([{by: '...', id:4848,..], [{by: 'adasd'], [{by: 'zzz}] ). the variable news is a whole tree while item its branches.
I just started working with the toolkit, so I don't fully understand this
Since item is probably an object from your Redux store, that thunk would try to modify a reference to your store - and modifying the store is only allowed in reducers.
Generally, you should be doing logic like this in reducers, not in a thunk.
So, do
export const fetchChildrenNews = createAsyncThunk('news/fetch1', async ([item, news]) => {
const res = await Promise.all(item.kids.map(id => {
let url = `https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`;
return fetch(url);
}));
const jsons = await Promise.all(res.map(r => r.json()));
return jsons
})
and then in your slice, add the logic:
builder.addCase(fetchChildrenNews, (state, action) => {
const jsons = action.payload
// here, do not modify `items`, but modify `state` - I would assume `items` is somewhere in here anyways?
})
does anyone have some bad experience with saving float via executeRaw batch operation? Or at least an idea of how to solve my problem: ExecuteRaw is saving numbers like 7.76e-322. Input is 157 and saved value is 7.76e-322.
const items = [{uniqueId: 1, price: 100},{uniqueId: 2, price: 200}];
const completeKeys: string[] = Object.keys(items[0]);
const updateFieldsMapper = (item: any) => {
return Prisma.sql`(${Prisma.join(
completeKeys.map((key: string) => item[key])
)})`;
};
const insertKeys = completeKeys.map((key) =>
key.toLocaleLowerCase() !== key ? `"${key}"` : `${key}`
);
let insertValues = completeKeys.map((item) => updateFieldsMapper(item));
const updateSet = completeKeys.reduce((updateSet: string[], key: string) => {
if (!ingnoredKeys.includes(key)) {
updateSet.push(`"${key}" = EXCLUDED."${key}"`);
}
return updateSet;
}, []);
try {
await prisma.$executeRaw`
INSERT INTO "Product" (${Prisma.raw(insertKeys.join(","))})
VALUES ${Prisma.join(insertValues)}
ON CONFLICT (uniqueId)
DO UPDATE SET ${Prisma.raw(updateSet.join(","))};`;
} catch (error) {
console.error(util.inspect(error, false, null, true));
Sentry.captureException(error);
}
Thank you very much
I have a non-primitive list which I would like to sort.
When I sort it, the UI thread is blocked and the app freezes for a few seconds.
I tried to avoid this by using dart's Isloate compute function but since the parameter sent to the compute function must be a primitive or a list/map of primitives (send method) it didn't work.
To conclude, is there any way to perform a list sort(non-primitive) without blocking the UI thread?
Edit: Clarification - I was trying to call a function through compute and I was passing a list of objects (which I got from a third party plugin) as an argument, those objects had a property of type Iterable and that caused everything to fail - make sure all the types are primitive or List/Map of primitives. With the answers I received and changing the type from Iterable to List it worked.
I'm not sure if I understood your question, but you can sort a list of non primitive elements like this:
final List<Element> elements = [
Element(id: 1),
Element(id: 7),
Element(id: 2),
Element(id: 0)
];
elements.sort((a, b) => a.compareTo(b));
// or
elements.sort((a, b) => a.id > b.id ? 1 : -1);
This would be a print(elements); output:
I/flutter ( 7351): [id: 0, id: 1, id: 2, id: 7]
And this would be the class Element
class Element {
final int id;
Element({this.id});
#override
String toString() => "id: $id";
int compareTo(Element other) => this.id > other.id ? 1 : -1;
}
Edit: To make this asynchronously, you could do this:
Future<List<Element>> asyncSort() async {
print("before sort: $elements");
elements = await compute(_sort, elements);
print("after sort: $elements");
return elements;
}
static List<Element> _sort(List<Element> list) {
list.sort((a, b) => a.compareTo(b));
return list;
}
print("before calling asyncSort(): $elements");
asyncSort();
print("after calling asyncSort(): $elements");
And this would be the output:
I/flutter ( 7351): before calling asyncSort(): [id: 1, id: 7, id: 2, id: 0]
I/flutter ( 7351): before sort: [id: 1, id: 7, id: 2, id: 0]
I/flutter ( 7351): after calling asyncSort(): [id: 1, id: 7, id: 2, id: 0]
I/flutter ( 7351): after sort: [id: 0, id: 1, id: 2, id: 7]
Edit2: If you want to send a compare function to compute, you could use a Map or a List of arguments with the list and the compare function and pass that instead of the list, because compute just takes one argument. Here is an example:
Future<List<Element>> asyncSort() async {
print("before sort: $elements");
Map args = {"list": elements, "compare": compare};
elements = await compute(_sortWith, args);
print("after sort: $elements");
return elements;
}
static List<Element> _sortWith(Map args) {
List<Element> list = args["list"];
Function(Element a, Element b) compare = args["compare"];
list.sort((a, b) => compare(a, b));
return list;
}
static int compare(Element a, Element b) {
return a.id > b.id ? 1 : -1;
}
This is how i use compute, just put all parameters to a list, then call it in a List of dynamic object:
image = await compute(getCropImage, [copyFaces, streamImg]);
imglib.Image getCropImage(List<dynamic> values) {
var face = values[0]; // copyFaces
var image = values[1]; // streamImg
}
I'm currently working with a Map in which the values are of type integer but I need to update the value of a key every time an action takes place. Example: if the Map is { "key1": 1 } after the actions takes place it should be {"key1":2} and so on. Here's my code:
void addToMap(Product product) {
if (_order.containsKey(product.name)) {
_order.update(product.name, (int) => _order[product.name]+1);
}
_order[product.name] = 1;
}
Where _order is the Map
You may use the following idiomatic approach in Dart:
map.update(
key,
(value) => ++value,
ifAbsent: () => 1,
);
This uses the built-in update method along with the optional ifAbsent parameter that helps set the initial value to 1 when the key is absent in the map. It not only makes the intent clear but also avoids pitfalls like that of forgetting to place the return statement that had been pointed out in the other answer.
Additionally, you may also wrap up the above method as an Extension to Map<dynamic, int>. This way also makes the call site look much less cluttered, as visible from the following demo:
extension CustomUpdation on Map<dynamic, int> {
int increment(dynamic key) {
return update(key, (value) => ++value, ifAbsent: () => 1);
}
}
void main() {
final map = <String, int>{};
map.increment("foo");
map.increment("bar");
map.increment("foo");
print(map); // {foo: 2, bar: 1}
}
Add return or the map will always get overridden by _order[product.name] = 1;
void addToMap(Product product) {
if (_order.containsKey(product.name)) {
_order.update(product.name, (int) => _order[product.name]+1);
return;
}
_order[product.name] = 1;
}