Stream of List with elements from another streams' current values - dart

I want to create a List Stream based on another elements Stream. The List Stream should yield a new list every time an element from the list emits a new value.
Something like this:
Stream<List<Model>> getListStream(List<int> ids) async* {
final List<Model> models = [];
for (var id in ids) {
getModelStream(id).listen((event) {
models.add(event);
});
}
yield models;
}
but it always yields an empty array.
I think the problem is probably the fact the it does not react to the event listener.
How do you deal with this kind of problem?

You're yielding an empty list which will later be filled in.
If you want to wait for the list to be filled in before yielding it, you'll have to do that.
Either:
Stream<List<Model>> getListStream(List<int> ids) async* {
for (var id in ids) {
yield await getModelStream(id).toList();
}
}
or, if you want to start all the streams immediately, and compute them in parallel, and then emit the values when they are done (not necessarily in the original ID order), then:
Stream<List<Model>> getListStream(List<int> ids) {
var controller = StreamController<List<Model>>(sync: true);
controller.onListen = () {
var count = 0;
for (var id in ids) {
count++;
getModelStream(id).toList().then((models) {
controller.add(models);
if (--count == 0) controller.close();
});
}
};
return controller.stream;
}

Related

Break down activity function

I have an Activity function that reads child elements of a parent in an organization as follows:
[FunctionName("ChildReaderFunction")]
public async Task<List<User>> GetChildren([ActivityTrigger] User parent)
{
var children = await GetChildrenAsync(parent);
return children;
}
public async Task<List<User>> GetChildrenAsync(User parent)
{
var allUsers = new List<User> { parent };
List<User> children = null;
children = await ExecuteQueryAsync("tableName", $"Parent eq '{parent.Id}'");
var taskIndex = 0;
var readTasks = new Task<List<User>>[children.Count(x => x.Childcount > 0)];
foreach (var child in children)
{
if (child.Childcount > 0)
{
readTasks[taskIndex++] = GetChildrenAsync(child);
}
else
{
allUsers.Add(child);
}
}
var validTasks = readTasks.Where(task => task != null).ToList();
if (validTasks.Count > 0)
{
foreach (var result in await Task.WhenAll(validTasks))
{
allUsers.AddRange(result);
}
}
Console.WriteLine($"Got {allUsers.Count} children for {parent.Id}");
return allUsers;
}
This works perfectly when I use premium plan with a timeout of 2 hours. I'm trying to convert this to a consumption plan with a timeout of 10 min. On testing out, I see timeout exception. Is there a way to breakdown this durable function and complete execution in 10 min?
I tried to update this logic by using a queue as follows:
[FunctionName("ChildReaderFunction")]
public async Task<List<User>> GetChildren([ActivityTrigger] User parent)
{
var allUsers = new List<User>();
var directReportEntities = new List<User>();
Queue<User> myQueue = new Queue<Person>();
myQueue.Enqueue(request.Parent);
while (myQueue.Any())
{
var current = myQueue.Dequeue();
if (current.Childcount > 0)
{
var children = await GetChildrenAsync(parent);
foreach (var child in children)
{
myQueue.Enqueue(child);
}
}
allUsers.Add(current);
}
Console.WriteLine($"Got {allUsers.Count} children for {parent.Id}");
return allUsers;
}
public async Task<List<User>> GetChildrenAsync(User parent)
{
return await ExecuteQueryAsync("tableName", $"Parent eq '{parent.Id}'");
}
This also gives a timeout exception. Any suggestions on what other approach I could try?
You might think about trying to figure out which parts of this method are slow. Perhaps it isn't the method itself that is slow but the query to the database. How many rows are you trying to download?
Also, you have a recursive call in your method. That may lead to many queries being executed. Can you think of a different way to grab the data all at once instead of a little bit at a time?

Dart Streams - How to combine previous and next element?

I have a simple widget subscribed to a Stream of elements.
Each time a new element is received I would like to get also the previous element and decide which one of them pass downstream.
Currently I am using the map operator to store the previous element and calculate the next, like this:
elements.map((e) {
if (this.previous == null) {
this.previous = e;
return e;
}
final next = merge(this.previous, e);
this.previous = e;
return next;
}).listen(...);
How can I do this better and avoid having this.previous?
If you use the rxdart package there is an extension method called pairwise which according to the documentation:
Emits the n-th and n-1th events as a pair. The first event won't be emitted until the second one arrives.
Then you should be able to do something along the lines of this:
elements.pairwise().map((pair) => merge(pair.first, pair.last)).listen(...);
Here is one possibility
void main() {
List list = [12, 24, 48, 60];
list.reduce((value, element) {
print(value + element); // Push to another list maybe?
return element;
});
}
If you are working with a stream try this
void main() {
var counterStream = Stream<int>.periodic(const Duration(seconds: 1), (x) => x)
.reduce((previous, element) {
print(previous + element); // Push to another stream maybe?
return element;
});
}

Future.then() is executed too early

I have a method which is supposed to return a Future, containing a list of groups.
This works fine as I can loop the list of groups in that method itself, but somehow list is returned before it could be filled. Surely this is an error on my part but I can't seem to grasp what I'm doing wrong.
Future< List<GroupData> > getGroups(String uniqueUserID) async
List<GroupData> groups = new List<GroupData>();
try {
var result = Firestore.instance
.collection("groups")
.where("members", arrayContains: uniqueUserID);
result.snapshots()
.listen (
(data) {
// Handle all documents one by one
for (DocumentSnapshot ds in data.documents)
{
List<String> members = new List<String>();
for (dynamic member in ds.data['members'])
{
members.add( member );
}
groups.add( new GroupData(ds.data['name'], ds.data['description'], ds.documentID.toString(), ds.data['admin'], members) );
}
}
);
} catch (exception)
{
print ('Something went wrong while fetching the groups of user ' + uniqueUserID + ' !');
}
return groups;
}
This method is being called using the method Future.then() but the list is empty while there should be several resuls (and there are, I can loop all items in the list in the above method and access/print their data). What am I missing?
The execution of your function is never locked. It doesn't wait until your listened stream finished.
There are a few solutions:
change stream.listen into await for (final item in stream)
add an await stream.done
Example:
before:
Stream<List<T>> stream;
stream.listen((list) {
for (final item in list) {
print(item);
}
});
after:
await for (final list in stream) {
for (final item in list) {
print(item);
}
}

How to select an item from a List in flutter

I have list from a model like this
amount:"12000"
dateTime:"19/07/2018"
detail:"Soto"
hashCode:853818549
id:1
name:"Theodorus"
I want to just select amount and add it to another list of string, but I'm always getting this error A value of type 'String' can't be assigned to a variable of type 'List<String>'. , I thinks its because im not doing it right, here is my code below
void setupList() async {
DebtDatabase db = DebtDatabase();
listCache = await db.getMyDebt();
setState(() {
filtered = listCache;
});
List<String> amount = new List<String>();
listCache.map((value) {
amount = value.amount; } );
//print(amount);
}
can anyone help me, so I can get list of ammount from this model list and then sum all the ammount?
The map function returns an iterable and you can then transform it into a list.
You should try something like this:
void setupList() async {
DebtDatabase db = DebtDatabase();
listCache = await db.getMyDebt();
setState(() {
filtered = listCache;
});
List<String> amount = listCache.map((value) => value.amount).toList();
//print(amount);
}

What is the best way to `yield` over a collection/list that only exists inside of a callback, in Dart?

I have a method (_all) that has a callback (_runInTxn) inside. The callback gets the list (rs) that I would like to yield over and return via _all's Stream. However, the callback isn't marked with async* (but the method is marked as async*).
What is the best way to yield over a collection/list that only exists inside of a callback?
Here is an example of what is correct and works:
Stream<String> _all() async* {
var sql = 'SELECT id,value FROM $storeName';
SqlResultSet resultSet;
await _runInTxn((txn) {
txn.executeSql(sql, [], (txn, rs) {
resultSet = rs;
});
});
for (var i = 0; i < resultSet.rows.length; ++i) {
var row = resultSet.rows.item(i);
yield row['value'];
}
}
I'm curious if there's a better way to write this code, so I can avoid a null resultSet. I'd really love to have my yield statement inside of the executeSql statement. Can I do a "non-local yield" ?
There was a similar discussion about using yield inside Future.forEach() and the answer was as far as I remember that this is not supported (couldn't find the discussion yet).
I guess it would be easier without async*
Stream<String> _all() {
var sql = 'SELECT id,value FROM $storeName';
var sc = new StreamController<String>();
_runInTxn((txn) {
txn.executeSql(sql, [], (txn, rs) {
rs.rows.forEach((row) => sc.add(row['value']));
});
}).then((_) => sc.close());
return sc.stream;
}
try at DartPad

Resources