I am trying to learn reactive programming, so forgive me if I ask a silly question. I'm also open to advice on changing my design.
I am working in scala-swing to display the results of a simulator. With one setting, a chart is displayed as a histogram; with the other setting the chart is displayed as the cumulative sum. (I'm probably using the wrong word; in the first setting you might have bin1=2, bin2=5, bin3=3; in the second setting the first height is 2, the second is 2 + 5, the third is 2 + 5 + 3, etc.). The simulator can be slow, so I originally used a Future to compute it, and the set the data into the chart. I decided to try a reactive approach, so my requirements are: 1. I don't want to recreate the data when I change the display mode, and 2. I want to set the Observable once for the chart and have the chart listen to the same Observable permanently.
I got this to work when I started the chain with a PublishSubject and the Future set the data into the start of the chain. When the display mode changed, I created a new PublishSubject().map(newRenderingLogic).subscribe(theChartsObservable). I am now trying to do what looks like the "right way," but it's not working correctly. I've tried to simplify what I have done:
val textObservable: Subject[String] = PublishSubject()
textObservable.subscribe(text => {
println(s"Text: ${text}")
})
var textSubscription: Option[Subscription] = None
val start = Observable.from(Future {
"Base text"
}).cache
var i = 0
val button = new Button() {
text = "Click"
reactions += {
case event => {
i += 1
if (textSubscription.isDefined) {
textSubscription.get.unsubscribe()
}
textSubscription = Some(start.map(((j: Int) => { (base: String) => s"${base} ${j}" })(i)).subscribe(textObservable))
}
}
}
On start, an Observable is created and logic to print some text is added to it. Then, an Observable with the generated data is created and a cache is added so that the result is replayed if the next subscription comes in after its results are generated. Then, a button is created. Then on button clicks a middle observable is chained with unique logic (it's a function that creates a function to append the value of i into the string, run with the current value of i; I tried to make something that couldn't just be reused) that is supposed to change with each click. Then the first Observable is subscribed to it so that the results of the whole chain end up being printed.
In theory, the cache operation takes care of not regenerating the data, and this works once, but onComplete is called on textObservable and then it can't be used again. It works if I subscribe it like this:
textSubscription = Some(start.map(((j: Int) => { (base: String) => s"${base} ${j}" })(i)).subscribe(text => textObservable.onNext(text)))
because the call to onComplete is intercepted, but this looks wrong and I wanted to know if there was a more typical way to do this, or architect it. It makes me think that I don't understand how this is supposed to be done if there isn't an out-of-the-box operation to do this.
Thank you.
I'm not 100% sure if I got the essence of your question right, but: if you have an Observable that may complete and you want to turn it into an Observable that never completes, you can just concatenate it with Observable.never.
For example:
// will complete after emitting those three elements:
val completes = Observable.from(List(1, 2, 3))
// will emit those three elements, but will never complete:
val wontComplete = completes ++ Observable.never
For fun I'm helping my school out by creating an app which has all class cancellations for student use. From my IT technician I got a quite complex structure containing class name, teacher, and other information looking like this:
3818,"20170217",5,752,64,"Rh",,"fr_2",,,,"iV5",,,"IS10a~IS10b~IS10c~IS10d","Z",,1,"IS10a~IS10b~IS10c~IS10d",C,201702161517,"-"
3819,"20170217",6,752,102,"Rh",,"fr",,,,"iB3","iB3",,"IT10a","Z",,0,"IT10a",,201702161517,"-"
3820,"20170217",8,752,119,"Rh",,"fr",,,,"iC1.2","iC1.2",,"IS6a","Z",,0,"IS6a",,201702161517,"-"
3821,"20170227",2,753,207,"Dd","Kru","sc",,,,"iB8","iB8",,"IS9b","Z",,2097152,"IS9b",,201702270804,"+~-"
3822,"20170227",3,753,8,"Dd",,"phH_1",,,,"iB8",,,"IS12~IT12","Z",,2097153,"IS12~IT12",C,201702270804,"-"
3823,"20170227",4,753,29,"Dd",,"phH_1",,,,"iB8",,,"IS11~IT11","Z",,2097153,"IS11~IT11",C,201702270804,"-"
3824,"20170227",5,753,30,"Dd",,"phH_1",,,,"iB8",,,"IS11~IT11","Z",,2097153,"IS11~IT11",C,201702270804,"-"
3825,"20170227",6,753,7,"Dd",,"phH_1",,,,"iB8",,,"IS12~IT12","Z",,2097153,"IS12~IT12",C,201702270804,"-"
3826,"20170227",7,753,327,"Dd",,"COV",,,,"AC1",,,,"Z",,2097153,,,201702270803,
3827,"20170227",8,753,46,"Dd",,"ph_1",,,,"iB8",,,"IS10a~IS10b~IS10c~IS10d~IT10a~IT10b","Z",,2097153,"IS10a~IS10b~IS10c~IS10d~IT10a~IT10b",C,201702270804,"-"
From this data I need to get various pieces, such as "20170217" and put them into an array for later use. How would I best do this? For anyone who cares, I added the full snippet below!
https://jsfiddle.net/pztwfsq1/
Since there is one dataset per line you can iterate through all lines. Split each line at , and you'll have an array of the information.
Similar to this (to give you an idea):
let row = "1,Peter,5,92,,Brooklyn"
let data = row.components(separatedBy: ",")
let name = data[1] // Peter
let location = data[5] // Brooklyn
I have a bare bones struct.
struct Transaction {
var value: String = ""
var date: String = ""
var title: String = ""
}
In my project, Transaction is used like so...
var transaction:Transaction = Transaction()
//loops 3 times
repeat {
let parsed = stringAndType(from:myParser)//-> (string:String, type:UInt8)
switch parsed.type {
case 1:
print("1 -- \(parsed.string")
transaction.value = parsed.string
case 2:
print("2 -- \(parsed.string)")
transaction.date = parsed.string
case 4:
print("4 -- \(parsed.string)")
transaction.title = parsed.string
default: break
}
} while myParser.isOk
print("Returning transaction: \(transaction)")
return transaction
In this code, a structure is created. The parser feeds the data to the switch, which assigns the parsed string to the appropriate Transaction variable. When I run the following code, the output indicates that the assignments to transaction.date and transaction.value are not sticking.
Output:
2 -- 12/22/2015
1 -- -5.00
4 -- RECURRING PAYMENT BACKBLAZE
Returning transaction: Transaction(value: "", date: "", title: "RECURRING PAYMENT BACKBLAZE")
There is complex buffering behind the scenes in stringAndType(). Looking at this as a C programmer, I really want to think that function might be the problem. However, I was under the impression that Swift strings are structures, and are therefore copy on write, just like an integer or double. Hoping you can provide insight. Thank you for your time.
Update 02/15/17
The logic in this code is functioning as expected. I think the problem here is memory management. To help drive this point, I've...
Removed the print lines from my switch
Added didSet {} to each variable in Transaction
Like so...
struct Transaction {
var value: String = "" {
didSet {
print("The VALUE has changed from \(oldValue) to \(value)")
}
}
//this is repeated appropriately for the other two variables
}
The given output rules out any logical issues. Output:
The DATE has changed from to 12/22/2015
The VALUE has changed from to -5.00
The TITLE has changed from to RECURRING PAYMENT BACKBLAZE
Returning transaction: Transaction(value: "", date: "", title: "RECURRING PAYMENT BACKBLAZE")
stringAndType() does read from an unsafe buffer, but it copies the bytes to a Data structure, and returns a string initialized from the COPIED data. I thought this would insulate me from safety issues...perhaps theres an implementation detail to String I'm missing?
Credit to #unkgd, you were on the right track with your initial answer before you retracted it. There was a logical error between the while parser.isOk loop and the parent loop (omitted to keep the code short) that was wiping Transaction between value and title being set. There was no indication of this happening in the output. The loop posted above was not looping three times, rather, it was looping once, but that single loop was iterated three times by the parent. Therefore, only the most recent parsed value was stored in the structure, in this case, title. Again, full credit to #unkgd (if you repost your answer and I will mark it as correct). Thanks to everyone for their ideas.