sequence recognition with reactive framework - system.reactive

I'm a newbye of Rx Framework and I'd like to have an help of making a console application that recognize the input sequence of string of "A" and "B"
using reactive framework.
Here's my code that shows the initial input sequence (source) and the derived sequence. The "A" and "B" sequence is recognized correctly but I want not to recognize it if I get some other string between "A" and "B":
for exmple "A" "C" "B" is not a valid sequence
Thanks a lot.
static void Main(string[] args)
{
var subj = new Subject<string>();
var source = subj.AsObservable();
source.Subscribe(Console.WriteLine);
var derived = from first in source.Where(x => x == "A")
from second in source.Where(x => x == "B")
select new[] { first, second };
derived.Subscribe(x => Console.WriteLine("Derived: " + x[0] + " " +x[1]));
subj.OnNext("A");
subj.OnNext("B");
subj.OnNext("C");
subj.OnNext("D");
subj.OnNext("E");
subj.OnNext("F");
Console.ReadLine();
}

Try changing your derived query to this:
var derived =
source
.Publish(ss => ss.Zip(ss.Skip(1), (a, b) => new [] { a, b }))
.Where(xs => xs.SequenceEqual(new [] { "A", "B" }));
Or to:
var derived =
source
.Buffer(2, 1)
.Where(xs => xs.SequenceEqual(new [] { "A", "B" }));

Related

Handling of a Dictionary Element in the context of "Array" notation

Struggling to achieve 2) below.
In the examples below, T is a concrete type. T could be String but the examples would then look even stranger.
Works:
var v = [ // v is a Dictionary with two Dictionary<String, T>.Element's
"x": T("x"), // Unfortunate since "x" has to be repeated
"y": T("y")
]
Desired syntax, intended to do the same as 1). Does not work:
var v = [
{ let s = "x"; // Attempting to use a closure to "inline" the local variable s
return (s: T(name: s)) // Using a tuple to return the Dictionary<String, T>.Element
}(),
{ let s = "y";
return (s: T(name: s))
}()
]
Xcode error for 2): Heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
Trying to fix 2) with explicit types. Does not work.
var v : Dictionary<String, T>.Element = [
{ let s = "x";
return Dictionary<String, T>.Element(s: T(name: s))
}(),
{ let s = "y";
return Dictionary<String, T>.Element(s: T(name: s))
}()
]
Xcode error for 3): Dictionary of type 'Dictionary<String, T>' cannot be initialized with array literal
This "MWE" example admittedly looks weird, but I am trying to understand how, in general, it may be possible to use a Dictionary Element (Key and Value together, as a hole) as if it were (informally speaking) an element of an Array.
If you have an array of keys and you want to create a dictionary out of it by mapping each key to another type, I'd suggest this way:
let keys = ["x", "y", "z"]
let dict = Dictionary(
uniqueKeysWithValues: keys.map { key in (key, T(key)) }
)
I am still not sure exactly what you want but I thought I add this solution to see if it is correct or at least something to discuss further
struct T {
let name: String
}
extension Dictionary where Key == String, Value == T {
init(values: Key...) {
self = values.reduce(into: [:]) { $0[$1] = T(name: $1) }
}
}
var dict = Dictionary(values: "x", "y")
An alternative solution when the init needs to be dynamic
extension Dictionary where Key == String, Value == T {
init(values: Key..., construct: (Key) -> T) {
self = values.reduce(into: [:]) { $0[$1] = construct($1) }
}
}
var dict = Dictionary(values: "x", "y") { T(name: $0)}
Sufficient solution:
var v = Dictionary<String, T>(uniqueKeysWithValues:
[ { let s = "x"; return (s, T(s)) }(),
{ let s = "y"; return (s, T(s)) }(),
]
)
That means, that you must use explicit Dictionary constructor, using uniqueKeysWithValues

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

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.

Swift- how to initialize struct instance with function or other pattern

This is a dumb example, but I can't think of the right way to avoid repeating myself when I try to initialize my struct instances below. Notice how they get the same initializer (not sure if that's the right phrase), but what would be another way to do this so i'm giving it a function or something like that instead of the same struct.init(...)?
struct InnerSt {
var a: String
var b: String
}
var myStructs: [InnerSt] = []
func assignVal() {
for item in ["dog", "cat", "fish"] {
let a: String = "I'm a"
var pets: String
let inner: InnerSt = InnerSt.init(a: a, b: item)
switch item {
case "dog":
pets = "hairy"
//print(inner.a + " " + inner.b + " and I'm " + pets) //this is like what I want to repeatedly do without the repetition
myStructs.append(inner) //this works nicely but obviously I miss adding the pets variable
case "cat":
pets = "furry"
//print(inner.a + " " + inner.b + " and I'm " + pets)
myStructs.append(inner)
case "fish":
pets = "scaly"
//print(inner.a + " " + inner.b + " and I'm " + pets)
myStructs.append(inner)
default: ()
}
}
}
assignVal()
print(myStructs)
To avoid writing a bunch of initialisers you could simply change your implementation as follows:
func assignVal() {
let a = "I'm a "
for item in [1, 2] {
let temp = InnerSt.init(a: a, b: item)
print(temp)
}
}
Basically, you do not need to switch because item is being assigned as you loop. It will be assigned the value of 1 on the first iteration and 2 on the second.
The benefits are:
The InnerSt initialiser is written once (even though it is called multiple times).
If your array [1, 2] grows (to say [1, 2, 3]) you would not need to add new case to your switch.
A few side notes that helped me in the beginning:
InnerSt.init(a: a, b: item) can be shortened to InnerSt(a: a, b: item). Nice for readability.
let a: String = "I'm a" can be shorted to let a = "I'm a". Swift has an excellent type inference system. In this case the complier will infer that a is of type String.
innserSt would be better named InnerSt. See Apple's excellent guidelines.
Revision after comments
Playground code:
var petsDescriptions: [String] = [] // array of string descriptions of the animals
var pets = ["dog", "cat", "fish", "deer"] // array of all animals
func assignVal() {
for pet in pets {
var surfaceFeeling: String = "" // variable to hold the surface feeling of the pet e.g. "hairy"
switch pet { // switch on the pet
case "dog":
surfaceFeeling = "hairy"
case "cat":
surfaceFeeling = "furry"
case "fish":
surfaceFeeling = "scaly"
default:
surfaceFeeling = "<unknown>"
}
let description = "I'm \(surfaceFeeling) and I'm a \(pet)" // construct the string description
petsDescriptions.append(description) // add it to the storage array
}
}
assignVal()
print(petsDescriptions)
Console output:
["I\'m hairy and I\'m a dog", "I\'m furry and I\'m a cat", "I\'m scaly and I\'m a fish", "I\'m <unknown> and I\'m a deer"]
Let me know if I answered your question correctly or need to add some more information.

Array typing issue in build macro

Note: My issue #4417 was closed, but I didn't want to be that guy who opens another issue for the same thing.
Based on #3132, [ { "a": 1, "b": 2 }, { "a": 2 } ] doesn't compile unless you specifically type it to Array<Dynamic> or whatever type encompasses both. That's fine I guess, but inside of the build macro below, there is nowhere for me to type the array, and I get an error.
In general, I can make map literal notation work using untyped (http://try.haxe.org/#3dBf5), but I can't do that here since my types haven't been constructed yet.
macro public static function test():Array<Field> {
var fields = Context.getBuildFields();
// parse the JSON
var o = Context.parseInlineString('{ "arr": [ { "a": 1, "b": 2 }, { "a": 2 } ] }', Context.currentPos());
// ["test" => json] map literal notation
var a = [{ expr : EBinop(OpArrow, macro $v { "test" }, o), pos : Context.currentPos() }];
// creates: "public var json:StringMap<Dynamic> = ['test' => json];"
var nf:Field = {
name : "json",
doc : "docs",
meta : [],
access : [APublic],
kind : FVar(macro : haxe.ds.StringMap<Dynamic>, { expr : EArrayDecl(a), pos : Context.currentPos() } ),
pos : Context.currentPos()
};
fields.push(nf);
return fields;
// error: Arrays of mixed types...
}
Without knowing ahead of time what the structure of the json is, is there anything I can do?
You can still use untyped, by constructing an intermediate EUntyped(o) expression (more simply macro untyped $o).
Alternatively, you can traverse the parsed object and add ECheckType to Dynamic expressions to every array, generating something like to ([...]:Array<Dynamic>).
The implementation of this would look something like calling the following checkTypeArrays function with your parsed o object, before building the map literal expression.
static function checkTypeArrays(e:Expr):Expr
{
return switch (e) {
case { expr : EArrayDecl(vs), pos : pos }:
macro ($a{vs.map(checkTypeArrays)}:Array<Dynamic>);
case _:
haxe.macro.ExprTools.map(e, checkTypeArrays);
}
}
An improvement to this would be to only wrap in (:Array<Dynamic>) the arrays that fail Context.typeof(expr).

How do I cycle through the entire alphabet with Swift while assigning values?

I am trying to cycle through the entire alphabet using Swift. The only problem is that I would like to assign values to each letter.
For Example: a = 1, b = 2, c = 3 and so on until I get to z which would = 26.
How do I go through each letter in the text field that the user typed while using the values previously assigned to the letters in the alphabet?
After this is done, how would I add up all the letters values to get a sum for the entire word. I am looking for the simplest possible way to accomplish this but works the way I would like it to.
edit/update: Xcode 12.5 • Swift 5.4
extension Character {
static let alphabetValue = zip("abcdefghijklmnopqrstuvwxyz", 1...26).reduce(into: [:]) { $0[$1.0] = $1.1 }
var lowercased: Character { .init(lowercased()) }
var letterValue: Int? { Self.alphabetValue[lowercased] }
}
extension String {
var wordValue: Int { compactMap(\.letterValue).reduce(0, +) }
}
Character("A").letterValue // 1
Character("b").letterValue // 2
Character("c").letterValue // 3
Character("d").letterValue // 4
Character("e").letterValue // 5
Character("Z").letterValue // 26
"Abcde".wordValue // 15
I'd create a function something like this...
func valueOfLetter(inputLetter: String) -> Int {
let alphabet = ["a", "b", "c", "d", ... , "y", "z"] // finish the array properly
for (index, letter) in alphabet {
if letter = inputLetter.lowercaseString {
return index + 1
}
}
return 0
}
Then you can iterate the word...
let word = "hello"
var score = 0
for character in word {
score += valueOfLetter(character)
}
Assign the letters by iterating over them and building a dictionary with letters corresponding to their respective values:
let alphabet: [String] = [
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
]
var alphaDictionary = [String: Int]()
var i: Int = 0
for a in alphabet {
alphaDictionary[a] = ++i
}
Use Swift's built-in Array reduce function to sum up the letters returned from your UITextViewDelegate:
func textViewDidEndEditing(textView: UITextView) {
let sum = Array(textView.text.unicodeScalars).reduce(0) { a, b in
var sum = a
if let d = alphaDictionary[String(b).lowercaseString] {
sum += d
}
return sum
}
}
I've just put together the following function in swiftstub.com and it seems to work as expected.
func getCount(word: String) -> Int {
let alphabetArray = Array(" abcdefghijklmnopqrstuvwxyz")
var count = 0
// enumerate through each character in the word (as lowercase)
for (index, value) in enumerate(word.lowercaseString) {
// get the index from the alphabetArray and add it to the count
if let alphabetIndex = find(alphabetArray, value) {
count += alphabetIndex
}
}
return count
}
let word = "Hello World"
let expected = 8+5+12+12+15+23+15+18+12+4
println("'\(word)' should equal \(expected), it is \(getCount(word))")
// 'Hello World' should equal 124 :)
The function loops through each character in the string you pass into it, and uses the find function to check if the character (value) exists in the sequence (alphabetArray), and if it does it returns the index from the sequence. The index is then added to the count and when all characters have been checked the count is returned.
Maybe you are looking for something like this:
func alphabetSum(text: String) -> Int {
let lowerCase = UnicodeScalar("a")..."z"
return reduce(filter(text.lowercaseString.unicodeScalars, { lowerCase ~= $0}), 0) { acc, x in
acc + Int((x.value - 96))
}
}
alphabetSum("Az") // 27 case insensitive
alphabetSum("Hello World!") // 124 excludes non a...z characters
The sequence text.lowercaseString.unicodeScalars ( lower case text as unicode scalar ) is filtered filter keeping only the scalars that pattern match ~= with the lowerCase range.
reduce sums all the filtered scalar values shifted by -96 (such that 'a' gives 1 etc.). reduce starts from an accumulator (acc) value of 0.
In this solution the pattern match operator will just check for the scalar value to be between lowerCase.start (a) and lowerCase.end (z), thus there is no lookup or looping into an array of characters.