Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mapNotNull and whereNotNull #548

Merged
merged 20 commits into from
May 27, 2022
Merged

Conversation

hoc081098
Copy link
Collaborator

@hoc081098 hoc081098 commented Feb 3, 2021

Because we are using NNBD, it makes sense to add mapNotNull and whereNotNull extensions.
Fixes #382

  • Add mapNotNull: returns a Stream containing only the non-null results of applying the given transform function to each element of the Stream.
  • Add whereNotNull: returns a Stream which emits all the non-null elements of the Stream, in their original emission order.
  • Refactor exports

@hoc081098 hoc081098 added the enhancement New feature or request label Feb 3, 2021
@hoc081098 hoc081098 mentioned this pull request Feb 3, 2021
@frankpepermans
Copy link
Member

I'm actually a bit on the fence with this one,
Either design the mapper so that it cannot return null, or accept that it can be mapped to null, with the latter the Stream Type would change to Nullable.

Basically any transformer could change type to Nullable, flatmap, switchmap, etc...

@hoc081098
Copy link
Collaborator Author

hoc081098 commented Feb 4, 2021

Some languages like Kotlin, Swift have mapNotNull/compactMap that allows mapper to return null, but flatMap does not.

// kotlin mapNotNull
inline fun <T, R : Any> Iterable<T>.mapNotNull(transform: (T) -> R?): List<R>

// kotlin flatMap
inline fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R>
// swift compactMap
func compactMap<ElementOfResult>(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]

// swift flatMap
func flatMap<SegmentOfResult>(_ transform: (Self.Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence
  • switchMap, flatMap, exhaustMap (hight order mapping operator) doesn't need a mapper that returns null because of returning empty Stream is enough.
Stream<R> f(Stream<R> Function(T))

Here, R can be nullable-type or non-nullable type, but Stream<R> is non-nullable type

  • But for map, its mapper can return anything (incl. null value if return type is nullable).
Stream<R> map(R Function(T))

R can be nullable-type or non-nullable type.

  • In some case, we want to map each elements of Stream to nullable value, and filter out null value, and then cast Stream to Stream of non-nullable type
Stream<String> getMessageString(Stream<Message> stream) {\
    String? mapping(Message msg) { ... }

    return stream.map(mapping).where((e) => e != null).cast<String>();
} 

or

Stream<String> getMessageString(Stream<Message> stream) {
    String? mapping(Message msg) { ... }

    return stream.map(mapping).whereType<String>();
} 

If we have mapNotNull, it is very convenient and easily to read

Stream<String> getMessageString(Stream<Message> stream) {\
    String? mapping(Message msg) { ... }

    return stream.mapNotNull(mapping);
} 

@hoc081098
Copy link
Collaborator Author

hoc081098 commented Feb 8, 2021

For anyone interested in those extensions, consider my package https://github.com/hoc081098/rxdart_ext 😃

@codecov-io
Copy link

codecov-io commented Feb 8, 2021

Codecov Report

Merging #548 (f825a31) into master (7a1c89b) will increase coverage by 0.06%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##           master     #548      +/-   ##
==========================================
+ Coverage   93.82%   93.88%   +0.06%     
==========================================
  Files          70       73       +3     
  Lines        2201     2223      +22     
==========================================
+ Hits         2065     2087      +22     
  Misses        136      136              

@frankpepermans
Copy link
Member

Sorry took a while on my end,
what do you want to do with this one?

Alternatively, we could also include them, but under a special import, like: 'package:rxdart/rxdart_ext.dart'?

@hoc081098
Copy link
Collaborator Author

hoc081098 commented Feb 27, 2021

Sorry took a while on my end,
what do you want to do with this one?

Alternatively, we could also include them, but under a special import, like: 'package:rxdart/rxdart_ext.dart'?

I think including it in rxdart is more useful, people can use easily it rather than a addition package 😃. Other Rx impl. like RxSwift also have compactMap, which acts like mapNotNull.

@Ascenio
Copy link

Ascenio commented Dec 26, 2021

Sorry, but I don't feel like this is a good idea. This just expands the surface of the API, turning it more difficult to the user to master rxdart. Also, this operations can already be made with current operators.

@grahamsmith
Copy link

@hoc081098 - your work here is very useful and I thank you for it.

@Ascenio - I disagree with you on this. Map-ing or where-ing where not null is pretty common in other languages Reactive or not. I understand the impulse not to make 100s of helpers all over the place but I don't think that applies in this circumstance. Having them would be massively useful day to day.

It would be great to see this merged.

@codecov-commenter
Copy link

codecov-commenter commented May 25, 2022

Codecov Report

Merging #548 (da1486a) into master (fc0083a) will increase coverage by 0.07%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##           master     #548      +/-   ##
==========================================
+ Coverage   93.52%   93.59%   +0.07%     
==========================================
  Files          74       76       +2     
  Lines        2284     2309      +25     
==========================================
+ Hits         2136     2161      +25     
  Misses        148      148              

@hoc081098 hoc081098 reopened this May 25, 2022
@hoc081098 hoc081098 added the under evaluation We are determining the bast way to approach this issue label May 25, 2022
@hoc081098
Copy link
Collaborator Author

hoc081098 commented May 26, 2022

Thanks @grahamsmith

IMO, mapNotNull & whereNotNull should be included in rxdart. Two operators are common and useful in other languages (Kotlin, Swift, ...) and other Rx/Reactive Stream implementations. (RxSwift, Reactor Mono, Reactor Flux, Rust filter_map, etc...).

These lend to very clean and performant code when the right use case arises. As a quick example: stringStream$.mapNotNull(int.tryParse)

Alternatives

1. asyncExpand

asyncExpand((v) {
  final i = int.tryParse(i);
  return i == null ? null : Stream.value(i);
});

too generic and expensive (more Stream objects, more resources).

2. map(...).whereType<T>() or map(...).where((v) => v != null).cast<T>()

map(int.tryParse).whereType<int>();

repetitive code, not ideal if the transformation is expensive (see reactor/reactor-core#2411 (comment)), expensive (more Stream objects, more resources)


See also

reactor/reactor-core#2638
reactor/reactor-core#2411

@hoc081098
Copy link
Collaborator Author

Sorry, but I don't feel like this is a good idea. This just expands the surface of the API, turning it more difficult to the user to master rxdart. Also, this operations can already be made with current operators.

I don't think that it expands the API, because many current operators in rxdart can be implemented via others, such as mapTo, mapTo(v) === v = ...; map(() => v), but in some cases, mapTo can optimize the performance. (avoid extra lambda/function)

@lambui09
Copy link

good. I think it is necessary.

@TyFancaoHuynh
Copy link

@hoc081098 I also created this extension myself to use. It will be helpful when it is available.

@hoc081098 hoc081098 merged commit a8acf0d into ReactiveX:master May 27, 2022
@hoc081098 hoc081098 deleted the map_not_null branch May 27, 2022 06:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request under evaluation We are determining the bast way to approach this issue
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add mapNotNull operator
8 participants