Checking a list and timing out if all entries not found in RxJava/RxKotlin - rx-java2

I have a scenario where I have a function, scanForTargets, that returns an Observable of type FoundNumber. In FoundNumber I just need an ID field I can grab out of it. As each element comes back in the scanResults Observable, I want to check to see if the name field matches one of the names on a target list. If so, then I want to emit that. For example, if I am looking for numbers 1, and 2, and scanForTargets() emits back 1, 2, 3, and 4, then I want scanForValues to emit back only 1 and 2.
The caveat is that I only want to continue doing this until either:
1) A time period elapses (in which case I throw and error)
2) All items on the String list are found before the timeout.
What I have so far looks like this, but I cannot get it to work for me mostly due to the shortcut of stopping once/if all of the targets are found before the timeout.
fun scanForValues(targetList: List<String>): Observable<FoundNumber> {
val scanResult = scanForTargets()
return scanResult.doOnNext {scanResult -> Log.d(TAG, "Found potential target: " + scanResult.name) }
.filter(TargetPredicate(targetList)) //See if it's one of those we want
.timeout(5, TimeUnit.SECONDS) //Wait a max of 5 seconds to find all items
.doOnError { Log.w(TAG, "Failed to scan"}") }
.map{s->scanResult.name}
}
class TargetPredicate(private val targetList: List<String>) : Predicate<ScanResult> { override fun test(scanResult: ScanResult): Boolean {
if(scanResult == null) {
return false
}
return scanResult.name in targetList
}
}
How can I also add the check to stop if I find all of the items in the list? I can't just add another predicate right?
Thanks.
Update: As requested, here is some data to show what I mean.
Let's say that the scanForTargets() and supporting code looks like this:
var emittedList: List<String?> = listOf(null, "0", "1", "2", "3")
fun scanForTargets(): Observable<FoundNumber> = Observable
.intervalRange(0, emittedList.size.toLong(), 0, 1, TimeUnit.SECONDS)
.map { index -> FoundNumber(emittedList[index.toInt()]) }
data class FoundNumber(val targetId: String?)
Now if scanForValues was called with a list of 1 and 2, then it should emit back an Observable of 1 and then 2.

No, it is not as simple as adding another filter.
A possible solution is to use scan to remove items from a set containing your targets, and complete when the set becomes empty.
Example:
val targets = listOf("a", "b", "c")
fun scanForTarget(): Observable<String> = Observable.just("a", "b")
fun scanForValues(targets: List<String>): Completable {
val initial = targets.toMutableSet()
return scanForTarget()
.timeout(5, TimeUnit.SECONDS)
.scan(initial) { acc, next -> acc.remove(next); acc }
.filter { it.isEmpty() }
.singleOrError()
.toCompletable()
}
Note: a Completable is a special type of publisher that can only signal onComplete or onError.
Update: response to question update.
The new example in your question won't work, because null values are not allowed in RxJava2.
Assuming you fix that, the following solution may help you.
fun scanForValues(targets: List<String>): Observable<String> {
val accumulator: Pair<Set<String>, String?> = targets.toSet() to null
return scanForTarget()
.timeout(5, TimeUnit.SECONDS)
.scan(accumulator) { acc, next ->
val (set, previous) = acc
val item = if (next in set) next else null
(set - next) to item // return set and nullable item
}
.filter { it.second != null } // item not null
.take(initial.size) // limit to the number of items
.map { it.second } // unwrap the item from the pair
.map { FoundNumber(it) } // wrap in your class
}
Instead of using only the Set<String> as the accumulator, now we also add the item.
The item is nullable, this allows us to check if a given item was present or not.
Notice that no null values are passed through the observable flow. In this case null values are wrapped inside Pair<Set<String>, String?> which are never null themselves.

Related

How to block the return until a timer expires using RxJava

I'm not seeing anything ever get returned by the scan. I know it's because the mutableList gets returned right away, but how do I block the return until the time expires?
Basically, all I want to do is fill up the mutable list for as long as the take() permits then return that mutableList to the calling function.
This is what I have tried.
private val timeoutScheduler: Scheduler = Schedulers.computation()
fun scanForAllDevicesStartingWith(devicePrefix: String): List<String> {
Log.d(TAG, "Scanning for devices starting with $devicePrefix")
val mutableList = mutableListOf<String>()
val result = scanForDevices()
.take(3, TimeUnit.SECONDS, timeoutScheduler)
.subscribe { scanResult ->
val name = scanResult.bleDevice.name
Logger.d(TAG, "Potential device named $name found")
if(name != null) {
if(name.startsWith(prefix = devicePrefix)) {
Logger.d(TAG, "Match found $name")
mutableList.plus(name)
}
}
}
return mutableList
}
private fun scanForDevices(): Observable<ScanResult>
= rxBleClient.scanBleDevices(
ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.build(),
ScanFilter.Builder()
.build())
}
OK, here it is boiled down for the next person who wants to do this kind of thing. In Rx, they have Singles which are Observables that just emit one value. In my case I needed a list of String values, so just need to use a Single of type List of type String. That gets just one element emitted that happens to be a list of Strings. The code looks like this...
fun returnAllDevicesStartingWith(devicePrefix: String): Single<List<String>> {
return scanForDevices()
.take(3, TimeUnit.SECONDS, timeoutScheduler)
.map { it.bleDevice.name }
.filter { it.startsWith(devicePrefix) }
.toList()
}
The function that calls it (written in Java instead of Kotlin) looks like this:
List<String> devices = bleUtility.returnAllDevicesStartingWith(prefix).blockingGet();
I tested it using a mocked function like this:
//Begin test code
var emittedList: List<String> = listOf("dev1-1", "dev1-2", "dev2-1", "dev2-2", "dev3-1", "dev3-2")
private fun scanForRoomDevices(): Observable<FoundDevice> = Observable
.intervalRange(0, emittedList.size.toLong(), 0, 1, TimeUnit.SECONDS, timeoutScheduler)
.map { index -> FoundDevice(emittedList[index.toInt()], BleDevice(emittedList[index.toInt()])) }
data class FoundDevice(val controllerId: String, val bleDevice: BleDevice)
data class BleDevice(val name: String)
Hope this helps others.

Is there a way to make a signal similar to combineLatest without needing all the signals to initially fire?

I have an array of signals
var signals = [Signal<ActionResult?, NoError>]()
where
enum ActionResult
case failed
case pending
case completed
}
I want to create a combined signal that returns true if one or more of the signals fires a .pending
let doesAnyOfTheActionsLoad = Signal.combineLatest(signals).map { values in
values.reduce(false, { (result, nextResult) -> Bool in
if result == true { return true }
if case .pending? = nextResult {
return true
}
return false
})
}
My only problem is that the combineLatest will only fire if all signals have fired at least once, and i need my signal to fire regardless if all signals have fired. Is there a way to do this in ReactiveSwift?
Try this:
let doesAnyOfTheActionsLoad = Signal.merge(signals).map { $0 == .pending}
If you want the signal to stay true after one .pending, then you need to store the current state with something like the scan operator:
let doesAnyOfTheActionsLoad = Signal.merge(signals).scan(false) { state, next in
if case .pending? = next {
return true
}
else {
return state
}
}
scan is like the "live" reactive version of reduce; it sends along the current result each time a new value comes in and is accumulated.
The other solutions are technically correct but I thought this might fit your use case better.
// Only ever produces either a single `true` or a single `false`.
let doesAnyOfTheActionsLoad =
SignalProducer<Bool, NoError>
.init(signals)
.flatten(.merge) // Merge the signals together into a single signal.
.skipNil() // Discard `nil` values.
.map { $0 == .pending } // Convert every value to a bool representing whether that value is `.pending`.
.filter { $0 } // Filter out `false`.
.concat(value: false) // If all signals complete without going to `.pending`, send a `false`.
.take(first: 1) // Only take one value (so we avoid the concatted value in the case that something loads).

How to test if n or more elements of a Collection or Sequence passes a condition without processing every element

I'd like to test if a number (n) or more elements of a Sequence or Collection will return true when passed to a function. I'm not interested in how many elements would return true, just if n or more would or would not. I can get the correct result with this code:
let result = collection.filter { test($0) }.count > (n-1)
But the test function is called once for each element of collection. Is there a better (or possibly 'lazy') way to do this?
I can do this manually but iterating over the collection something like:
let result:Bool = {
var nCount = 0
for object in collection {
if test(object) {
nCount = nCount + 1
if nCount >= n {
return true
}
}
}
return false
}()
But the first way seems a lot more elegant.
I understand that, in a worst-case scenario, every element would have to be tested. I'm just like to avoid the unnecessary computation if possible.
In the case n=1 (check if at least one element of the sequence passes
the test) you can use contains(where:) with a predicate:
let result = sequence.contains(where: { test($0) } )
or just
let result = sequence.contains(where: test)
In the general case (check if the sequence contains at least a given number of matching items) you can use a lazy filter. Example:
func isEven(_ i : Int) -> Bool { return i % 2 == 0 }
let numbers = Array(1...10)
let atLeast4EvenNumbers = !numbers.lazy.filter(isEven).dropFirst(3).isEmpty
print(atLeast4EvenNumbers)
If you add a print statement to the isEven function then you'll see
that it is not called more often than necessary.

LinkedList in Scala

For exercise I'm trying to implement a LinkedList in Scala.
Main problem is about Null reference.
But first some code:
class Node(xkey: String, xnext: Option[Node], xinfo: Int) {
val key: String = xkey;
var next = xnext.getOrElse(None);
var info: Int = xinfo;
def this(xkey: String, xinfo: Int) {
this(xkey, None, xinfo);
}
def this(xkey: String) {
this(xkey, None, -1);
}
#Override
override def toString: String = key + ":" + info
}
At this point, I'm already concerned about things.
I declare xnext in construct as a Option[Node], because the tail in this linkedList does not have a next.
In my first try, it was just a Node, but had problem with null object because compilator just told me that "null can't cast to Node" (or something like that, I do not remember now) - And so I switch to this Option.
But, is it ok? Because, you know, for me next should be a Node, not a Option, otherwise, I don't know, in the linkedList how to reference to next Node.
Whatever, second class (i.e. my Linked List)
class LinkedNode {
private var first: Option[Node] = None;
private var last: Option[Node] = None;
def addNode(newNode: Node) = {
if (first == null) {
first = Some(newNode);
last = Some(newNode);
first.next = last;
}
else {
last.next = newNode;
newNode.next = null;
last = newNode
}
}
def size(): Long = {
var currentNode : = first;
var size = 0L;
while (currentNode != null) {
size+=1;
currentNode = currentNode.next;
}
size
}
def findNodeByKey(key: String) : Node = {
var currentNode = first;
while(currentNode != null) {
if (currentNode.key.equals(key))
currentNode
else {
currentNode = currentNode.next;
}
}
currentNode;
}
def delNodeByKey(key : String) : Boolean = {
var currentNode = first;
var previousNode = first;
while(currentNode != null) {
if (currentNode.key.equals(key)) {
previousNode = currentNode.next;
return true;
}
previousNode = currentNode;
currentNode = currentNode.next;
}
return false;
}
}
And nothing. I'm already block to my constructor because first and last.
How should I declare them? Node? Or Option[Node]?
Problems are also in Add method.
When I add a node, I want to add a Node object, not an Option[Node].
And I don't get how to achieve things I want with all Option, Some and None classes.
I know I should not be so vague with my request, but any help?
P.S. I've already read this Q/A and it didn't help me
At this point, I'm already concerned about things. I declare xnext in construct as a Option[Node], because the tail in this linkedList does not have a next.
[...]
But, is ok? because, you know, for me next should be a Node, not a Option, otherwise, I don't know, in the linkedList how to reference to next Node.
This is a good solution to replacing null, which you definitely want to do to prevent null-pointer exceptions and the like. An Option[Node] is simply a Node wrapped in a container (or None). You can check whether or not it has a value with isEmpty or get its value with get (which will throw an exception if the Option is empty).
The only difference to null, as you'd use it in Java, is that you need to check if it isEmpty instead of checking for null, and that you need to unwrap (option.get) it explicitly when you're sure that it is not None.
A more paradigmatic (scala-typical) way of retrieving the value from an option is pattern matching:
option match {
case Some(x) => println(x)
case None => println("Whoops, no value :(")
}
Regarding your other questions, they are indeed a little vague.
How should I declere them? Node? or Option[Node]?
Use Option[Node] if the possibility exists that there's no value for the variable (i.e., if you'd set it to null sometimes in Java).
When I add a node, I want to add a Node object, not a Option[Node].
No, you want to add an Option[Node] internally, because you will need to check later on if a node is set or not. In Scala it is preferrable to do this via Option[Node].isEmpty compared to setting things to null. You're already doing this in some parts of your code (e.g., addNode), where you do Some(newNode) (I'd call this "wrapping the node in an Option", but I'm not entirely sure if that's the correct terminology).
And I don't get how to achieve things I want with all Option, Some and None class.
What you're doing in your addNode does seem correct to a degree, but you somehow try to use null again in the else branch. What about:
// We don't need Option[Node] for the parameter, because there
// _must_ be a Node value to be added
def addNode(newNode: Node) = {
if (first.isEmpty) {
first = Some(newNode)
last = Some(newNode)
first.next = last
} else {
newNode.next = None
last.next = Some(newNode)
last = Some(newNode)
}
}
(I didn't run that code, nor did I do an thorough check of your logic)

Why does map(_:) in Swift Playground return a String and not a tuple?

I am attempting to use Swift Playground to use map(_:) and enumerated() to walk through an array of orders, returning the first perfect match to a customers goods.
However, when testing in Swift Playground; the map(_:) function returns a string when it should be a tuple.
I'm attempting to retrieve the index and the value; of a given array filter.
Right now, my current solution is this;
let orders = [4,2,7]
let goods = 2
var matching:Int = (orders.filter{ $0 == goods }.first) ?? 0 as Int
In this example, the answer is 2; however it doesn't give me the index of the array.
My second attempt in Swift Playground is thus
var r = (orders.filter{ $0 == goods }).enumerated().map { (index, element) -> (Int,Int) in
return (index, element)
}
print (r.first!) // This should report (0,2)
However, this in Swift Playground prints out in the sidebar panel
"(0, 2)\n"
Screenshot:
Why does the sidebar reporting that this is a string?
Is there a way to get the index and element correctly in this example?
Get Index of Orders that matches Goods
To get index & element:
var r = orders.enumerated().map { ( index, element) -> (Int, Int) in
return (index, element)
}.filter { (index, element) -> Bool in
if element == goods {
return true
}
return false
}
or more compact:
var r = orders.enumerated().map { index, element in (index, element) }
.filter { _, element in element == goods ? true : false }
print("r: \(r.first)")
Prints:
r: (1, 2)
If you really want to find the first match only, a for loop is more efficient, as you can break after the first match is found.
Playground
What you see "(0, 2)\n" is the result of print. What it prints out in console is (0, 2) plus newline.
If you want to see the actual value of r.first!in the sidebar, remove the print:
print (r.first!)
r.first!
Result:
Others have already covered the answer, this is just a side note. I suggest you break down your statement to make it clearer.
var r = orders.filter{ $0 == goods }
.enumerated()
.map{ index, element in (index, element) }
The print statement puts your output in "" and also adds the linebreak \n at the end.
If you write r.first!, you will see that it actually is a tuple.