Related
I have two Publishers A and B. They are imbalanced as in A will emit 3 values, then complete, B will only emit 1 value, then complete (A actually can emit a variable number, B will remain 1 if that helps):
A => 1, 2, 3
B => X
B also runs asynchronously and will likely only emit a value after A already emitted its second value (see diagram above). (B might also only emit any time, including after A already completed.)
I'd like to publish tuples of A's values combined with B's values:
(1, X) (2, X) (3, X)
combineLatest is not up for the job as it will skip the first value of A and only emit (2, X) and (3, X). zip on the other hand will not work for me, because B only emits a single value.
I am looking for an elegant way to accomplish this. Thanks!
Edit and approach to a solution
A bit philosophical, but I think there is fundamental question if you want to go the zip or combineLatest route. You definitely need some kind of storage for the faster publisher to buffer events while you wait for the slower to start emitting values.
One solution might be to create a publisher that collects events from A until B emits and then emits all of the collected events and continues emitting what A gives. This is actually possible through
let bufferedSubject1 = Publishers.Concatenate(
prefix: Publishers.PrefixUntilOutput(upstream: subject1, other: subject2).collect().flatMap(\.publisher),
suffix: subject1)
PrefixUntilOutput will collect everything until B emits (subject2) and then switch to just regularly passing the output of it.
However if you run
let cancel = bufferedSubject1.combineLatest(subject2)
.sink(receiveCompletion: { c in
print(c)
}, receiveValue: { v in
print(v)
})
you are still missing the first value from A (1,X) -- this seems to be a bit like a race condition: Will bufferedSubject1 have all values emitted first or does subject2 provide a value to combineLatest first?
What I think is interesting is that without any async calls, the behavior seems to be undefined. If you run the sample below, sometimes™️ you get all values emitted. Sometimes you are missing out on (1,X). Since there is no async calls and no dispatchQueue switching here, I would even assume this is a bug.
You can "dirty fix" the race condition by providing a delay or even just a receive(on: DispatchQueue.main) between bufferedSubject1 and combineLatest, so that before we continue the pipeline, we hand back control to the DispatchQueue and let subject2 emit to combineLatest.
However, I would not deem that elegant and still looking for a solution that uses zip semantics but without having to create an infinite collection of the same value (which does not play well with sequential processing and unlimited demand, the way I see it).
Sample:
var subject1 = PassthroughSubject<Int, Never>()
var subject2 = PassthroughSubject<String, Never>()
let bufferedSubject1 = Publishers.Concatenate(prefix: Publishers.PrefixUntilOutput(upstream: subject1, other: subject2).collect().flatMap(\.publisher),
suffix: subject1)
let bufferedSubject2 = Publishers.Concatenate(prefix: Publishers.PrefixUntilOutput(upstream: subject2, other: subject1).collect().flatMap(\.publisher),
suffix: subject2)
let cancel = bufferedSubject1.combineLatest(subject2)
.sink(receiveCompletion: { c in
print(c)
}, receiveValue: { v in
print(v)
})
subject1.send(1)
subject1.send(2)
subject2.send("X")
subject2.send(completion: .finished)
subject1.send(3)
subject1.send(completion: .finished)
Ok, this was an interesting challenge and though it seemed deceptively simple, I couldn't find a simple elegant way.
Here's a working approach (though hardly elegant) that seems to not suffer from the race condition of using PrefixUntilOutput/Concatenate combo.
The idea is to use combineLatest, but one that emits as soon as the first publisher emits, with the other value being nil so that we don't lose the initial values. Here's a convenience operator that does that that I called combineLatestOptional:
extension Publisher {
func combineLatestOptional<Other: Publisher>(_ other: Other)
-> AnyPublisher<(Output?, Other.Output?), Failure>
where Other.Failure == Failure {
self.map { Optional.some($0) }.prepend(nil)
.combineLatest(
other.map { Optional.some($0) }.prepend(nil)
)
.dropFirst() // drop the first (nil, nil)
.eraseToAnyPublisher()
}
}
Armed with the above, the second step in the pipeline uses Scan to collect values into an accumulator until the other publisher emits the first value. There are 4 states of the accumulator that I'm representing this state with a State<L, R> type:
fileprivate enum State<L, R> {
case initial // before any one publisher emitted
case left([L]) // left emitted; right hasn't emitted
case right([R]) // right emitted; left hasn't emitted
case final([L], [R]) // final steady-state
}
And the final operator combineLatestLossless is implemented like so:
extension Publisher {
func combineLatestLossless<Other: Publisher>(_ other: Other)
-> AnyPublisher<(Output, Other.Output), Failure>
where Failure == Other.Failure {
self.combineLatestOptional(other)
.scan(State<Output, Other.Output>.initial, { state, tuple in
switch (state, tuple.0, tuple.1) {
case (.initial, let l?, nil): // left emits first value
return .left([l]) // -> collect left values
case (.initial, nil, let r?): // right emits first value
return .right([r]) // -> collect right values
case (.left(let ls), let l?, nil): // left emits another
return .left(ls + [l]) // -> append to left values
case (.right(let rs), nil, let r?): // right emits another
return .right(rs + [r]) // -> append to right values
case (.left(let ls), _, let r?): // right emits after left
return .final(ls, [r]) // -> go to steady-state
case (.right(let rs), let l?, _): // left emits after right
return .final([l], rs) // -> go to steady-state
case (.final, let l?, let r?): // final steady-state
return .final([l], [r]) // -> pass the values as-is
default:
fatalError("shouldn't happen")
}
})
.flatMap { status -> AnyPublisher<(Output, Other.Output), Failure> in
if case .final(let ls, let rs) = status {
return ls.flatMap { l in rs.map { r in (l, r) }}
.publisher
.setFailureType(to: Failure.self)
.eraseToAnyPublisher()
} else {
return Empty().eraseToAnyPublisher()
}
}
.eraseToAnyPublisher()
}
}
The final flatMap creates a Publishers.Sequence publisher from all the accumulated values. In the final steady-state, each array would just have a single value.
The usage is simple:
let c = pub1.combineLatestLossless(pub2)
.sink { print($0) }
zip on the other hand will not work for me, because B only emits a single value.
Correct, so fix it so that that’s not true. Start a pipeline at B. Using flatmap turn its signal into a publisher for a sequence of that signal, repeated. Zip that with A.
Example:
import UIKit
import Combine
func delay(_ delay:Double, closure:#escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
class ViewController: UIViewController {
var storage = Set<AnyCancellable>()
let s1 = PassthroughSubject<Int,Never>()
let s2 = PassthroughSubject<String,Never>()
override func viewDidLoad() {
super.viewDidLoad()
let p1 = s1
let p2 = s2.flatMap { (val:String) -> AnyPublisher<String,Never> in
let seq = Array(repeating: val, count: 100)
return seq.publisher.eraseToAnyPublisher()
}
p1.zip(p2)
.sink{print($0)}
.store(in: &storage)
delay(1) {
self.s1.send(1)
}
delay(2) {
self.s1.send(2)
}
delay(3) {
self.s1.send(3)
}
delay(2.5) {
self.s2.send("X")
}
}
}
Result:
(1, "X")
(2, "X")
(3, "X")
Edit
After stumbling on this post I wonder if the problem in your example is not related to the PassthroughSubject:
PassthroughSubject will drop values if the downstream has not made any demand for them.
and in fact using :
var subject1 = Timer.publish(every: 1, on: .main, in: .default, options: nil)
.autoconnect()
.measureInterval(using: RunLoop.main, options: nil)
.scan(DateInterval()) { res, interval in
.init(start: res.start, duration: res.duration + interval.magnitude)
}
.map(\.duration)
.map { Int($0) }
.eraseToAnyPublisher()
var subject2 = PassthroughSubject<String, Never>()
let bufferedSubject1 = Publishers.Concatenate(prefix: Publishers.PrefixUntilOutput(upstream: subject1, other: subject2).collect().flatMap(\.publisher),
suffix: subject1)
let cancel = bufferedSubject1.combineLatest(subject2)
.sink(receiveCompletion: { c in
print(c)
}, receiveValue: { v in
print(v)
})
subject2.send("X")
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
subject2.send("Y")
}
I get this output :
(1, "X")
(2, "X")
(3, "X")
(3, "Y")
(4, "Y")
(5, "Y")
(6, "Y")
And that seems to be the desired behavior.
I don't know if it is an elegant solution but you can try to use Publishers.CollectByTime :
import PlaygroundSupport
import Combine
PlaygroundPage.current.needsIndefiniteExecution = true
let queue = DispatchQueue(label: "com.foo.bar")
let cancellable = letters
.combineLatest(indices
.collect(.byTimeOrCount(queue, .seconds(1), .max))
.flatMap { indices in indices.publisher })
.sink { letter, index in print("(\(index), \(letter))") }
indices.send(1)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
indices.send(2)
indices.send(3)
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
letters.send("X")
}
DispatchQueue.main.asyncAfter(deadline: .now() + 3.3) {
indices.send(4)
}
DispatchQueue.main.asyncAfter(deadline: .now() + 3.5) {
letters.send("Y")
}
DispatchQueue.main.asyncAfter(deadline: .now() + 3.7) {
indices.send(5)
indices.send(6)
}
Output :
(X, 1)
(X, 2)
(X, 3)
(Y, 3)
(Y, 4)
(Y, 5)
(Y, 6)
Algorithmically speaking, you need to:
wait until B emits an event, collect all elements that A emits
store the element you just received from B
emit the pair of elements emitted so far by A
emit the rest of elements that A emits after B emitted it's element
An implementation of the above algoritm can be done like this:
// `share` makes sure that we don't cause unwanted side effects,
// like restarting the work `A` does, as we subscribe multiple
// times to this publisher
let sharedA = a.share()
// state, state, state :)
var latestB: String!
var cancel = sharedA
// take all elements until `B` emits
.prefix(untilOutputFrom: b.handleEvents(receiveOutput: { latestB = $0}))
// wait on those elements
.collect()
// uncollect them
.flatMap { $0.publisher }
// make sure we deliver the rest of elements from `A`
.append(sharedA)
// now, pair the outputs together
.map { ($0, latestB) }
.sink(receiveValue: { print("\($0)") })
Maybe there's a way to avoid the state (latestB), and use a pure pipeline, couldn't yet find it, though.
P.S. As an added bonus, if B is expected to emit more than one element, than with a simple change we can support this scenario too:
let sharedA = a.share()
let sharedB = b.handleEvents(receiveOutput: { latestB = $0}).share()
var latestB: String!
var cancel = sharedA.prefix(untilOutputFrom: sharedB)
.collect()
.flatMap { $0.publisher }
.append(sharedA)
.map { ($0, latestB)}
.sink(receiveValue: { print("\($0)") })
I am trying to compare multiple variables to an expression, like so:
if 1 <= x && x <= 5 &&
1 <= y && y <= 5 &&
1 <= z && z <= 5 {
// Code to run if true
}
I found a question related to comparing a variable to multiple specific values, which is not what I want because it is not a comparison in an inequality.
Is there any way I can shorten this, in any way?
For example making an inequality like 1 <= x && x <= 5 shortened, or making me able to compare x, y, and z easily in other ways?
Use ranges!
if (1...5).contains(x) &&
(1...5).contains(y) &&
(1...5).contains(z) {
}
Alternatively, create a closure that checks whether something is in range:
let inRange: (Int) -> Bool = (1...5).contains
if inRange(x) && inRange(y) && inRange(z) {
}
As Hamish has suggested, the allSatisfy method in Swift 4.2 can be implemented as an extension like this:
extension Sequence {
func allSatisfy(_ predicate: (Element) throws -> Bool) rethrows -> Bool {
return try !contains { try !predicate($0) }
}
}
Another option: match against a tuple of ranges:
if case (1...5, 1...5, 1...5) = (x, y, z) {
}
Or use a switch-statement to match against one or more
tuples of ranges:
switch (x, y, z) {
case (1...5, 1...5, 1...5):
print("all between 1 and 5")
case (..<0, ..<0, ..<0):
print("all negative")
default:
break
}
(Compare Can I use the range operator with if statement in Swift?.)
Maybe like this:
if [x,y,z].compactMap{ (1...5).contains($0) }.contains(true) {
//Do stuff
}
List data = [1, 2, 3];
data.forEach((value) {
if (value == 2) {
// how to stop?
}
print(value);
});
I tried return false; which works in jQuery, but it does not work in Dart.
Is there a way to do it?
You can also use a for/in, which implicitly uses the iterator aptly demonstrated in the other answer:
List data = [1,2,3];
for(final i in data){
print('$i');
if (i == 2){
break;
}
}
It is also possible to implement your example using forEach() and takeWhile().
var data = [1, 2, 3];
data.takeWhile((val) => val != 2).forEach(print);
Breaking a List
List<int> example = [ 1, 2, 3 ];
for (int value in example) {
if (value == 2) {
break;
}
}
Breaking a Map
If you're dealing with a Map you can't simply get an iterator from the given map, but you can still use a for by applying it to either the values or the keys. Since you sometimes might need the combination of both keys and values, here's an example:
Map<String, int> example = { 'A': 1, 'B': 2, 'C': 3 };
for (String key in example.keys) {
if (example[key] == 2 && key == 'B') {
break;
}
}
Note that a Map doesn't necessarily have they keys as [ 'A', 'B', 'C' ] use a LinkedHashMap if you want that. If you just want the values, just do example.values instead of example.keys.
Alternatively if you're only searching for an element, you can simplify everything to:
List<int> example = [ 1, 2, 3 ];
int matched = example.firstMatching((e) => e == 2, orElse: () => null);
The callback that forEach takes returns void so there is no mechanism to stop iteration.
In this case you should be using iterators:
void listIteration() {
List data = [1,2,3];
Iterator i = data.iterator;
while (i.moveNext()) {
var e = i.current;
print('$e');
if (e == 2) {
break;
}
}
}
Dart does not support non-local returns, so returning from a callback won't break the loop.
The reason it works in jQuery is that each() checks the value returned by the callback.
Dart forEach callback returns void.
http://docs.jquery.com/Core/each
based on Greg Lowe post, I used where for my project and also it works.
var data = [1, 2, 3];
data.where((val) => val != 2).forEach(print);
Using Multiple Loop
Break Outer Loop
OUTER: for (var i = 0; i < m.length; i++) {
for (var j = 0; j < m[i].length; j++) {
if (m[i][j] < 0) {
print("Negative value found at $i,$j: ${m[i][j]}");
break OUTER;
}
}
}
Continue Outer Loop
outer: for (var v in a) {
for (var w in b) {
if (w == v) continue outer;
}
print(v);
}
Here is a full sample by for-in loop, that close to forEach style.
void main(){
var myList = [12, 18, 24, 63, 84,99];
myList.forEach((element) {
print(element);
if (element ==24); //break; // does not work
});
for(var element in myList) {
print(element);
if (element==24) break;
}
}
Somebody suggest where() but it is not a general replacement for forEach() with break capability
(where is however a correct replacement for the use case showed in the example of the question. I, on the other hand, focus on the question in the title)
The functionality of foreach() but with an equivalent of break, is given by any(): to continue the loop you return false, to stop you return true; the result of any() can be ignored. I think it is more similar to each() in jquery (but in dart to stop you return true).
To have a loop with the index, but also the possibility in case of break the loop, I use the following extension:
extension IterableUtils<E> on Iterable<E> {
/**
Similar to Iterable.forEach() but:
- with an index argument
- with the optional capacity to break the loop, returning false
Note: as for the return clause, you can omit it, as with forEach()
*/
void forEachIndexed(Function(E element, int index) f) {
int index = 0;
for (E element in this) {
if (f(element, index) == false) break;
index++;
}
}
}
Example:
void main() {
List list = ["a", "b", "c"];
list.forEachIndexed((element, index) {
print("$index: $element");
//Optional:
if (element == "b") return false; //break
});
}
You CAN empty return from a forEach to break the loop;
List<int> data = [1, 2, 3];
int _valueToBePrinted;
data.forEach((value) {
if (value == 2) {
_valueToBePrinted = value;
return;
}
});
// you can return something here to
// return _valueToBePrinted;
print(value);
anyway you shouldn't...
the catch is, you can't return anything in the entire forEach loop
//This don't work
data.forEach((value) {
if (value == 2) {
_valueToBePrinted = value;
return;
}
if (value == 1) {
return value;
}
});
I was solving a problem in which given a linked list of characters , we have to move the vowels to the beginning such that both vowels and consonants are in chronological order. That is in the order in which they appear in original list.
Input : S->T->A->C->K->O->V->E->R->F->L->O->W
Output : A->O->E->O->S->T->C->K->V->R->F->L->W
I did it by traversing through the list once and created two lists called vowels and consonants and later merged them.
Can it be done without creating extra lists ? I mean in-place maybe using pointer manipulation?
Remember the beginning of the list. When you meet a vowel, move it to the beginning of the list; the vowel becomes the new beginning that you remember.
1. Traverse the list
2. When you encounter a vowel, check with head if its smaller or greater
3. If smaller, re-place new vowel before head, else move head and check again
4. In the end relocate head to first
temp = head;
while(current.next != null) {
if(current.isVowel()) {
if(head.isVowel()) {
//check the precedence
//Re-place the current with temp
}
else {
//Re-place current in front of head
}
}
current = current.next;
}
This is an abstract understanding. Implement it properly.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
struct list {
struct list *next;
int ch;
};
#define IS_VOWEL(p) strchr("aeiouy", tolower(p->ch))
struct list *shuffle ( struct list *lst )
{
struct list *new=NULL, **dst, **src;
dst = &new;
for (src = &lst; *src; ) {
struct list *this;
this= *src;
if (!IS_VOWEL(this)) { src= &(*src)->next; continue; }
*src = this->next;
this->next = *dst;
*dst = this;
dst = & (*dst)->next;
}
*dst = lst;
return new;
}
int main (void)
{
struct list arr[] = { {arr+1, 'S'} , {arr+2, 'T'} , {arr+3, 'A'}
, {arr+4, 'C'} , {arr+5, 'K'} , {arr+6, 'O'}
, {arr+7, 'V'} , {arr+8, 'E'} , {arr+9, 'R'}
, {arr+10, 'F'} , {arr+11, 'L'} , {arr+12, 'O'} , {NULL, 'W'} };
struct list *result;
result = shuffle (arr);
for ( ; result; result = result->next ) {
printf( "-> %c" , result->ch );
}
printf( "\n" );
return 0;
}
OUTPUT:
-> A-> O-> E-> O-> S-> T-> C-> K-> V-> R-> F-> L-> W
You can quite easily modify pointers to create two independent lists without actually having to duplicate any of the nodes, which is what I assume you mean when you say you want to avoid creating new lists. Only the pointers in the original nodes are modified.
First let's create the structures for the list:
#include <stdio.h>
#include <stdlib.h>
// Structure for singly linked list.
typedef struct sNode {
char ch;
struct sNode *next;
} tNode;
And next we provide two utility functions, the first to append a character to the list:
// Append to list, not very efficient but debug code anyway.
static tNode *append (tNode *head, char ch) {
// Allocate new node and populate it.
tNode *next = malloc (sizeof (tNode));
if (next == NULL) {
puts ("Out of memory");
exit (1);
}
next->ch = ch;
next->next = NULL;
// First in list, just return it.
if (head == NULL)
return next;
// Else get last, adjust pointer and return head.
tNode *this = head;
while (this->next != NULL)
this = this->next;
this->next = next;
return head;
}
And the second to dump a list for debugging purposes:
// Debug code to dump a list.
static void dump (tNode *this) {
if (this == NULL)
return;
printf ("(%08x)%c", this, this->ch);
while ((this = this->next) != NULL)
printf (" -> (%08x)%c", this, this->ch);
putchar ('\n');
}
Beyond that, we need an easy way to tell if a node is a vowel or not. For our purposes, we'll only use uppercase letters:
// Check for vowel (uppercase only here).
static int isVowel (tNode *this) {
char ch = this->ch;
return (ch == 'A') || (ch == 'E') || (ch == 'I')
|| (ch == 'O') || (ch == 'U');
}
Now this is the important bit, the bit that turns the single list into two distinct lists (one vowel, one consonant). Which list is which type depends on what the first entry in the list is.
What is basically does is to create a sub-list out of all the common nodes at the start of the list ("ST" in this case), another sub-list of the next non-matching type ("A"), and then starts processing the remaining nodes one by one, starting with "C".
As each subsequent node is examined, the pointers are adjusted to add it to either the first or second list (again, without actually creating new nodes). Once we reach the NULL at then end of the list, we then decide whether to append the second list to the first, or vice versa (vowels have to come first).
The code for all this pointer manipulation is shown below:
// Meat of the solution, reorganise the list.
static tNode *regroup (tNode *this) {
// No reorg on empty list.
if (this == NULL)
return this;
// Find first/last of type 1 (matches head), first of type 2.
tNode *firstTyp1 = this, *firstTyp2 = this, *lastTyp1 = this, *lastTyp2;
while ((firstTyp2 != NULL) && (isVowel (firstTyp1) == isVowel (firstTyp2 ))) {
lastTyp1 = firstTyp2;
firstTyp2 = firstTyp2->next;
}
// No type 2 means only one type, return list as is.
if (firstTyp2 == NULL)
return firstTyp1;
// Type 2 list has one entry, next node after that is for checking.
lastTyp2 = firstTyp2;
this = firstTyp2->next;
//dump (firstTyp1);
//dump (firstTyp2);
//putchar ('\n');
// Process nodes until list is exhausted.
while (this != NULL) {
// Adjust pointers to add to correct list.
if (isVowel (this) == isVowel (lastTyp1)) {
lastTyp2->next = this->next;
lastTyp1->next = this;
lastTyp1 = this;
} else {
lastTyp1->next = this->next;
lastTyp2->next = this;
lastTyp2 = this;
}
// Advance to next node.
this = this->next;
//dump (firstTyp1);
//dump (firstTyp2);
//putchar ('\n');
}
// Attach last of one list to first of the other,
// depending on which is the vowel list.
if (isVowel (firstTyp1)) {
lastTyp1->next = firstTyp2;
return firstTyp1;
}
lastTyp2->next = firstTyp1;
return firstTyp2;
}
And, finally, no complex program would be complete without a test harness of some description, so here it is, something to create and dump the list in its initial form, then reorganise it and dump the result:
int main (void) {
char *str = "STACKOVERFLOW";
tNode *list = NULL;
while (*str != '\0')
list = append (list, *(str++));
dump (list);
puts("");
list = regroup (list);
dump (list);
return 0;
}
Upon entering, compiling and running all that code, the results are as expected:
(09c03008)S -> (09c03018)T -> (09c03028)A -> (09c03038)C ->
(09c03048)K -> (09c03058)O -> (09c03068)V -> (09c03078)E ->
(09c03088)R -> (09c03098)F -> (09c030a8)L -> (09c030b8)O ->
(09c030c8)W
(09c03028)A -> (09c03058)O -> (09c03078)E -> (09c030b8)O ->
(09c03008)S -> (09c03018)T -> (09c03038)C -> (09c03048)K ->
(09c03068)V -> (09c03088)R -> (09c03098)F -> (09c030a8)L ->
(09c030c8)W
In case that's hard to read, I'll get rid of the pointers and just list the characters in order:
S -> T -> A -> C -> K -> O -> V -> E -> R -> F -> L -> O -> W
A -> O -> E -> O -> S -> T -> C -> K -> V -> R -> F -> L -> W
I'm having issues grouping date range results in couch db.
Say I have this data:
2010-11-14, Tom
2010-11-15, Tom
2010-11-15, Dick
2010-11-15, Tom
2010-11-20, Harry
and i want use a view (and possibly reduce function) to return grouped names between 2010-11-14 and 2010-11-16, eg
Tom 3
Dick 1
how can this be
achieved?
I would suggest the following document structure, and map and reduce functions:
{ date : '2010-11-14', name : 'Tom' }
function(doc) { var r = {}; r[doc.name] = 1; emit (doc.date, r); }
function (keys, values, rereduce) {
var r = {};
for (var i in values) {
for (var k in values[i]) {
if (k in r) r[k] += values[i][k];
else r[k] = values[i][k];
}
}
return r;
}
Then, you would query the view, asking for a full reduce (no grouping) with startkey and endkey parameters 2010-11-14 and 2010-11-16. You will get in return a single value:
{ 'Tom': 3, 'Dick': 1 }