create a list stream from a map stream - dart

I have a project that requires me to work with streams.
I have a Stream of a Map with the following code:
Stream<Map<String,TextBlock>> get phoneNumberBlocks =>
visionMatch.map((MatchVisionTextRegex ourRegexDetection) {
final Map<String,TextBlock> list = <String,TextBlock>{};
for (MatchVisionTextData textData in ourRegexDetection.phone) {
list[textData.parsedText]=textData.textBlock;
}
return list;
}).asBroadcastStream();
now I want to create another stream that takes the phoneNumberBlocks stream of map, and returns another stream of a list of the keys of that map.
so far I have this:
Stream<List<String>> get phoneNumbers =>
phoneNumberBlocks.map<String,TextBlock>((String s, TextBlock b) =>
s).asBroadcastStream();
which is totally broken and doesn't work! :) any ideas how to achieve this ?
thank you

You can use a StreamTransformer since its purpose it is to receive a stream, transform it and return a new one.
So given a Stream of Map<String,String>:
StreamController<Map<String,String>> streamController = StreamController.broadcast();
Stream<Map<String,String>> get phoneNumberBlocks => streamController.stream;
You can create a new one like this:
Stream<List<String>> get phoneNumbers => phoneNumberBlocks.transform(StreamTransformer.fromHandlers(
handleData: (Map<String,String> data, sink) {
sink.add(data.keys.toList());
}
));
The StreamTransformer will receive the data from phoneNumberBlocks and add only the keys from the map into the new Stream of List<String>.
A practical example:
void main() {
phoneNumbers.listen((s) => print(s));
streamController.sink.add({
'1': 'a',
'2': 'b',
'3': 'c',
});
}
Console:
[1, 2, 3]
See it in action here.

Related

ng2-pdf-viewer / pdfjs annotation change listener

ng2-pdf-viewer provides a pdfjs PDFDocumentProxy on load with some useful properties such as getFieldObjects(). These return the annotation layer fields with their initial values.
I however can't find an observable to subscribe onto changes. There are several eventBus._listeners objects when I console.log the PDFDocumentProxy during the onLoad function, but I don't think any of these trigger on input change.
Is there a better way to analyze pdf.js annotation fields than to iterate over all field objects and read the html inputs like:
public async onLoad(pdf: PDFDocumentProxy): void {
const obj = await pdf.getFieldObjects();
const names: string[] = [];
Object.keys(obj).forEach((o: string) => names.push(
...obj[o].filter(element => element['name'])
.map(x => x['name'])));
names.forEach((name: string) => (document.getElementsByName(name)[0] as HTMLInputElement).value;
}

Multiple Symfony form types, all for same object, validate at once by faking submission

I have several FormTypes, all for the same entity type (Car, e.g.), each handles a different aspect (DoorsTyoe, EngineType, TrunkType, SeatsType). When the user has walked through all these forms, on different pages, I want to check before finally saving the results, once more all FormTypes.
So the idea was: Create a new FormType CarType, put all the different pieces on it (DoorsType, EngineType, ...), "fake a submit" and get the errors. But when I want to add the different "sub-forms", I cannot add them without specifying a "child name" for them. Is there any way around this? Right now, I create an array with all the child names, and have a the car object in a separate key:
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add(
'doors',
DoorsType::class,
[
'getter' => function ($data, FormInterface $form): Car {
return $data['theCar'];
},
'setter' => function (array $data, Car $submittedData, FormInterface $form): void {
// just throw away what we get.
},
],
);
// And so on for EngineType etc.
}
Then I call this with:
$fooData = [
"doors" => "PLACEHOLDER",
"engine" => "PLACEHOLDER",
"theCar" => $car
];
$form = $this->formFactory->create($formType, $fooData, [
]);
$form->submit($fooData, true);
$errors = $form->getErrors(true, true);
dump($errors);
return $form->isValid();
My issue is the crazy structure with the $fooData array. I would prefer to work on $car only. When I change the DoorsType etc. to unmapped fields, the getters are not called so I assume they will not work at all?
Somehow I am missing a point...

Push objects into array in Dart

List returnMovies = [];
Future<List> _getData() async {
final response = await http.get("https:../getTodayMovies",
headers: {HttpHeaders.AUTHORIZATION: Acess_Token.access_token});
if (response.body != null) {
returnMovies = json.decode(response.body);
.....
setState(() {});
} else {
final responseUpcoming = await http.get("/upcoming",
headers: {HttpHeaders.AUTHORIZATION: Acess_Token.access_token});
returnMovies = json.decode(responseUpcoming.body);
}
The response.body looks like:
[{"id":394470548,"host_group_name":"heyab redda","movie_image":"..png","type":"horror","code":"X123","name":"Lovely","start_time":1554364800,"end_time":1554393600,"}]
The responseUpcoming.body looks like:
{"id":394470545,"host_group_name":"foo redda","movie_image":".png","type":"horror","code":"X123","name":"Lovely","start_time":1554364800,"end_time":1554393600,"}, {"id":394470548,"host_group_name":"foo1 redda","movie_image":"..png","type":"comic","code":"X125","name":"Lovely1","start_time":1554364800,"end_time":1554393600,"}
The error I get is: String, dynamic is not a subtype of type List<dynamic>.
In the first API call that I am doing I normally get in return an array of objects, however, when this is empty, the second API call returns a list of objects that I want to push into the array called returnMovies, how can I achieve this?? Is there any way to .push these objects in the array?? So then I want to use this array to build dynamically a Listview.builder.
Also is it correct the way I am declaring it? I am quite new on Dart. Thank you
Sounds like you are looking for addAll
returnMovies.addAll(json.decode(returnUpcoming.body))
I will suggest to use
returnMovies.addAll({your object here})
When you do this json.decode(response.body) you are getting a List of Map you should use List<dynamic> movieListData and get the items like this:
movieListData = json.decode(response.body);
returnMovies = movieListData.map((dynamic movieData) {
String id = movieData['_id'];
String host_group_name = movieData['host_group_name'];
String duration = movieData['duration'];
return new Movie(id,title, duration);
}).toList();

how to get multiple streams based on the same stream controller in flutter?

I have a Bloc class that needs three streams based on the same stream controller.
class TodoBloc {
final _todoController = StreamController<List<TodoModel>>();
get allTodoStream => _todoController.stream;
get activeTodoStream => _todoController.stream
.map<List<TodoModel>>(
(list) => list.where((item) => item.state == 0));
get completedTodoStream => _todoController.stream
.map<List<TodoModel>>(
(list) => list.where((item) => item.state == 1));}
it's a list of to-dos which have states. I'd like to retrieve the to-dos with an active state in a stream separate from the one that retrieves the other states.
I have a method that's responsible for the filtering and returns a stream depending on the filter value. here's the method:
Stream<List<TodoModel>> filterTodoLs(String filter) {
if (filter == 'all') {
return todoStream;
} else if (filter == 'completed') {
return completedStream;
} else if (filter == 'active') {
return activeStream;
}
return todoStream;
}
later to be used in a widget like the following:
return StreamBuilder<List<TodoModel>>(
stream: bloc.filterTodoLs(filter),
builder:(BuildContext context, AsyncSnapshot<List<TodoModel>> todoSnapShot) {}
the snapshot is empty so far. how can i filter my original stream and return different ones depending on the filter applied to that stream?
A StreamController.broadcast() creates a stream that can have multiple listeners.
See https://www.dartlang.org/tutorials/language/streams#broadcast-streams
The details of switching between the different filtered streams depends on context not provided in the question.

Create a new stream from a stream in Dart

I suspect that my understanding of Streams in Dart might have a few holes in it...
I have a situation in which I'd like a Dart app to respond to intermittent input (which immediately suggests the use of Streamss -- or Futures, maybe). I can implement the behavior I want with listener functions but I was wondering how to do this in a better, more Dartesque way.
As a simple example, the following (working) program listens to keyboard input from the user and adds a div element to the document containing what has been typed since the previous space, whenever the space bar is hit.
import 'dart:html';
main() {
listenForSpaces(showInput);
}
void listenForSpaces(void Function(String) listener) {
var input = List<String>();
document.onKeyDown.listen((keyboardEvent) {
var key = keyboardEvent.key;
if (key == " ") {
listener(input.join());
input.clear();
} else {
input.add(key.length > 1 ? "[$key]" : key);
}
});
}
void showInput(String message) {
document.body.children.add(DivElement()..text = message);
}
What I'd like to be able to do is to create a new Stream from the Stream that I'm listening to (in the example above, to create a new Stream from onKeyDown). In other words, I might set the program above out as:
var myStream = ...
myStream.listen(showInput);
I suspect that there is a way to create a Stream and then, at different times and places, insert elements to it or call for it to emit a value: it feels as though I am missing something simple. In any case, any help or direction to documentation would be appreciated.
Creating a new stream from an existing stream is fairly easy with an async* function.
For a normal stream, I would just do:
Stream<String> listenForSpaces() async* {
var input = <String>[];
await for (var keyboardEvent in document.onKeyDown) {
var key = keyboardEvent.key;
if (key == " ") {
yield input.join();
input.clear();
} else {
input.add(key.length > 1 ? "[$key]" : key);
}
}
}
The async* function will propagate pauses through to the underlying stream, and it may potentially pause the source during the yield.
That may or may not be what you want, since pausing a DOM event stream can cause you to miss events. For a DOM stream, I'd probably prefer to go with the StreamController based solution above.
There are several methods and there is a whole package rxdart to allow all kinds of things.
Only the final consumer should use listen and only if you need to explicitly want to unsubscribe, otherwise use forEach
If you want to manipulate events like in your example, use map.
I wasn't originally planning to answer my own question but I have since found a very simple answer to this question in the dartlang creating streams article; in case it's helpful to others:
Specifically, if we'd like to create a stream that we can insert elements into at arbitrary times and places in the code, we can do so via the StreamController class. Instances of this class have an add method; we can simply use the instance's stream property as our stream.
As an example, the code in my question could be rewritten as:
import 'dart:html';
import 'dart:async';
main() async {
// The desired implementation stated in the question:
var myStream = listenForSpaces();
myStream.listen(showInput);
}
Stream<String> listenForSpaces() {
// Use the StreamController class.
var controller = StreamController<String>();
var input = List<String>();
document.onKeyDown.listen((keyboardEvent) {
var key = keyboardEvent.key;
if (key == " ") {
// Add items to the controller's stream.
controller.add(input.join());
input.clear();
} else {
input.add(key.length > 1 ? "[$key]" : key);
}
});
// Listen to the controller's stream.
return controller.stream;
}
void showInput(String message) {
document.body.children.add(DivElement()..text = message);
}
(As mentioned in the article, we need to be careful if we want to set up a stream from scratch like this because there is nothing to stop us from inserting items to streams that don't have associated, active subscribers; inserted items would in that case be buffered, which could result in a memory leak.)

Resources