Queue operator for RxJS - rxjs

Is there an operator in RxJS that would allow me to buffer items and let them out one by one whenever a signal observable fires? Sort of like bufferWhen, but instead of dumping the whole buffer on each signal it would dump a certain number per signal. It could even dump the number that gets emitted by the signal observable.
Input observable: >--a--b--c--d--|
Signal observable: >------1---1-1-|
Count in buffer: !--1--21-2-121-|
Output observable: >------a---b-c-|

Yes, you can use zip to do what you want:
const input = Rx.Observable.from(["a", "b", "c", "d", "e"]);
const signal = new Rx.Subject();
const output = Rx.Observable.zip(input, signal, (i, s) => i);
output.subscribe(value => console.log(value));
signal.next(1);
signal.next(1);
signal.next(1);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs#5/bundles/Rx.min.js"></script>
In fact, zip is used as an example in this GitHub issue that pertains to buffering.
If you want to use the signal's emitted value to determine how many buffered values are to be released, you could do something like this:
const input = Rx.Observable.from(["a", "b", "c", "d", "e"]);
const signal = new Rx.Subject();
const output = Rx.Observable.zip(
input,
signal.concatMap(count => Rx.Observable.range(0, count)),
(i, s) => i
);
output.subscribe(value => console.log(value));
signal.next(1);
signal.next(2);
signal.next(1);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs#5/bundles/Rx.min.js"></script>

window can be used to separate the timeline. And takeLast is used to hold the output.
let signal = Rx.Observable.interval(1000).take(4);
let input = Rx.Observable.interval(300).take(10).share();
let output = input
.do(value => console.log(`input = ${value}`))
.window(signal)
.do(() => console.log(`*** signal : end OLD and start NEW subObservable`))
.mergeMap(subObservable => {
return subObservable.takeLast(100);
})
.share()
output.subscribe(value => console.log(` output = ${value}`));
Rx.Observable.merge(input.mapTo(1), output.mapTo(-1))
.scan((count, diff) => {
return count + diff;
}, 0)
.subscribe(count => console.log(` count = ${count}`));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs/bundles/Rx.min.js"></script>
Result:
22:28:37.971 *** signal : end OLD and start NEW subObservable
22:28:38.289 input = 0
22:28:38.292 count = 1
22:28:38.575 input = 1
22:28:38.576 count = 2
22:28:38.914 input = 2
22:28:38.915 count = 3
<signal received>
22:28:38.977 output = 0
22:28:38.979 count = 2
22:28:38.980 output = 1
22:28:38.982 count = 1
22:28:38.984 output = 2
22:28:38.986 count = 0
22:28:38.988 *** signal : end OLD and start NEW subObservable
22:28:39.175 input = 3
22:28:39.176 count = 1
22:28:39.475 input = 4
22:28:39.478 count = 2
22:28:39.779 input = 5
22:28:39.780 count = 3
<signal received>
22:28:39.984 output = 3
22:28:39.985 count = 2
22:28:39.986 output = 4
22:28:39.988 count = 1
22:28:39.989 output = 5
22:28:39.990 count = 0
22:28:39.992 *** signal : end OLD and start NEW subObservable
22:28:40.075 input = 6
22:28:40.077 count = 1
22:28:40.377 input = 7
22:28:40.378 count = 2
22:28:40.678 input = 8
22:28:40.680 count = 3
22:28:40.987 input = 9
22:28:40.990 count = 4
<input completed>
22:28:40.992 output = 6
22:28:40.993 count = 3
22:28:40.995 output = 7
22:28:40.996 count = 2
22:28:40.998 output = 8
22:28:40.999 count = 1
22:28:41.006 output = 9
22:28:41.007 count = 0

Related

RxJs combineLatest subscribe never fires

Script
import { forkJoin, zip, combineLatest, Subject, ReplaySubject } from 'rxjs';
import { withLatestFrom, take, first } from 'rxjs/operators';
const letters: string = 'abcdefghijklmnopqrstuvwxyz';
function getLetter(i: number) : string {
const l: string = letters[i].toString();
console.log(` letter ${l}`);
return letters[i].toString();
}
function getNumber(i: number) : string {
const l: string = letters[i].toString();
console.log(` number ${i}`);
return i.toString();
}
const sa = new ReplaySubject<string>(1);
const sz = new ReplaySubject<string>(1);
combineLatest(sa, sz)
.subscribe(([a, z]) => {console.log(`s: ${a}-${z}`);});
var i: number = 0;
var b: boolean = true;
while(i < 10) {
console.log(`i: ${i}`);
sa.next(getNumber(i));
if(b) {
sa.next(getLetter(i));
}
i++;
b = !b;
}
Output
C:\Work\ts-experiments>tsc
C:\Work\ts-experiments>node dist\index.js
i: 0
number 0
letter a
i: 1
number 1
i: 2
number 2
letter c
i: 3
number 3
i: 4
number 4
letter e
i: 5
number 5
i: 6
number 6
letter g
i: 7
number 7
i: 8
number 8
letter i
i: 9
number 9
C:\Work\ts-experiments>
Why nothing from the .subscribe?
(Unfortunately this doesn't work in JSFiddle.)
Because sz never fires. For combineLatest to fire, all of the observables should emit at least once.

tick format produces overlapping labels

I am currently using:
var formatValue = d3.format("s");
and then
var axisX = d3.svg.axis()
.scale( someXscale )
.tickFormat (function( d ) { return formatValue(d); } );
This code produces the following when I zoom (from the highest zoom to the lowest):
The values on this axis can go up to 3,100,000,000.
I don't like the fact that the values fall on top of each other and less important I would like the labels to have Giga, Mega, Kilo.
I would appreciate any suggestions.
A good way can be drawn from this comment by M. Bostock:
var formatAbbreviation = d3.format(".2s");
formatAbbreviation(5000000000); // 5.00G
formatAbbreviation(5000000); // 5.00M
formatAbbreviation(5000); // 5.00k
In the post you see him actually customising the behaviour, changing G to B.
I actually created my own custom implementation in the past, maybe it can be useful.
Here's an example I've made:
var format = function(num) {
var numToStr = num + '';
var ext = ['', 'K', 'M', 'G']; // Add extensions as needed
var size = 3;
var val = 0;
var max = 5; // how many digit maximum we want on screen
var compress = function(str) {
var len = str.length;
if (len <= size) {
return str + ext[val];
}
if (val + 1 === ext.length) {
return str.slice(0, max) + ext[val]; // <= what to do when the max number of digits is reached, change as needed
}
val++;
return compress(str.slice(0, str.length - size));
}
return compress(numToStr);
}
console.log(format(1)) // => 1
console.log(format(12)) // => 12
console.log(format(123)) // => 123
console.log(format(1234)) // => 1K
console.log(format(12345)) // => 12K
console.log(format(123456)) // => 123K
console.log(format(1234567)) // => 1M
console.log(format(12345678)) // => 12M
console.log(format(123456789)) // => 123M
console.log(format(1234567890)) // => 1G
console.log(format(12345678901)) // => 12G
console.log(format(12345678902321312)) // => 12345G
// used more or less like so in d3
var axisX = d3.svg.axis()
.scale( someXscale )
.tickFormat (function( d ) { return format(d); } );
As you notice we can define the maximum number of digits you want on screen - in this case 5 - and handle that case as we think it's best for the particular situation (every case is different).

SpriteKit for loop

Hi I'm trying to follow a tutorial on Ray Wenderlich site
[http://www.raywenderlich.com/76740/make-game-like-space-invaders-sprite-kit-and-swift-tutorial-part-1][1]
so I'm going thru the functions breaking it down so i can get an understanding of how it works I've commented out stuff which i think i understand but this bit has me stumped
thanks for looking
the for loop whats the var row = 1 at the beginning doing ?
I've only ever done for lops like
for Position in 0...9
{
// do something with Position ten times
}
then whats the % in if row %3 mean?
for var row = 1; row <= kInvaderRowCount; row++ // start of loop
{
var invaderType: InvaderType // varible of atype etc
if row % 3 == 0
{
invaderType = .AType
} else if row % 3 == 1
hers the rest of the code
func makeInvaderOfType(invaderType: InvaderType) -> (SKNode) // function passes in a enum of atype,btype,ctype and returns sknode
{
var invaderColor: SKColor// variable for the colour
switch(invaderType)// switch statment if we pass in atype we will get red
{
case .AType:
invaderColor = SKColor.redColor()
case .BType:
invaderColor = SKColor.greenColor()
case .CType:
invaderColor = SKColor.blueColor()
default:
invaderColor = SKColor.blueColor()
}
let invader = SKSpriteNode(color: invaderColor, size: kInvaderSize)//variable of a skspritenode with color from switch statement size from vairiabe kinvadersize
invader.name = kInvaderName // name is invader fron let kinvadername
return invader //return the spritenode with color size name
}
func setupInvaders()
{
let baseOrigin = CGPoint(x:size.width/3, y:180) // vairible to hold cgpoint screen size /3 width 180 height
for var row = 1; row <= kInvaderRowCount; row++ // start of loop
{
var invaderType: InvaderType // varible of atype etc
if row % 3 == 0
{
invaderType = .AType
} else if row % 3 == 1
{
invaderType = .BType
} else
{
invaderType = .CType
}
let invaderPositionY = CGFloat(row) * (kInvaderSize.height * 2) + baseOrigin.y// varible to hold cgfloat row ? think its the incriment of the for loop times 16 times 2 = 32 plus 180 first time is 212 then 244
/* so if ive got his rightthe sum goes row = 1 kinvadersize.hieght *2 = 32 + baseoringin.y = 180
1 * 32 +180 = 212
2 * 32 + 180 = 392 but its 244
*/
println(row)
var invaderPosition = CGPoint(x:baseOrigin.x, y:invaderPositionY) // varible to hold cgpoint
println(invaderPosition.y)
for var col = 1; col <= kInvaderColCount; col++
{
var invader = makeInvaderOfType(invaderType)// varible that runs function and return the spritenode with color size name????
invader.position = invaderPosition
addChild(invader)
invaderPosition = CGPoint(x: invaderPosition.x + kInvaderSize.width + kInvaderGridSpacing.width, y: invaderPositionY)
}
}
}
If I understand your question correctly, here's the answer. Based on this code:
for var row = 1; row <= kInvaderRowCount; row++ // start of loop
{
var invaderType: InvaderType // varible of atype etc
if row % 3 == 0
{
invaderType = .AType
} else if row % 3 == 1
The first line means:
var row = 1: given a new variable, row, with a value of 1
row <= kInvaderRowCount: as long as the variable row is less than or equal to kInvaderRowCount, keep running the for loop
row++: after each time the loop is run, increment (increase) the value of row by 1
As for the "%", that is the modulo operator. It returns the remainder after a division operation on integer values. So if 7 divided by 3 = 2, with a remainder of 1, then
7 / 3 = 2
7 % 3 = 1
The modulus operator results in an integer. While 1 / 3 = 0.33..., 1 % 3 = 1. Because the remainder of 1 divided by 3 is 1.
1 % 3 = 1
2 % 3 = 2
3 % 3 = 0
4 % 3 = 1
5 % 3 = 2
6 % 3 = 0
see also: How Does Modulus Divison Work.

Algorithm to generate a sequence proportional to specified percentage

Given a Map of objects and designated proportions (let's say they add up to 100 to make it easy):
val ss : Map[String,Double] = Map("A"->42, "B"->32, "C"->26)
How can I generate a sequence such that for a subset of size n there are ~42% "A"s, ~32% "B"s and ~26% "C"s? (Obviously, small n will have larger errors).
(Work language is Scala, but I'm just asking for the algorithm.)
UPDATE: I resisted a random approach since, for instance, there's ~16% chance that the sequence would start with AA and ~11% chance it would start with BB and there would be very low odds that for n precisely == (sum of proportions) the distribution would be perfect. So, following #MvG's answer, I implemented as follows:
/**
Returns the key whose achieved proportions are most below desired proportions
*/
def next[T](proportions : Map[T, Double], achievedToDate : Map[T,Double]) : T = {
val proportionsSum = proportions.values.sum
val desiredPercentages = proportions.mapValues(v => v / proportionsSum)
//Initially no achieved percentages, so avoid / 0
val toDateTotal = if(achievedToDate.values.sum == 0.0){
1
}else{
achievedToDate.values.sum
}
val achievedPercentages = achievedToDate.mapValues(v => v / toDateTotal)
val gaps = achievedPercentages.map{ case (k, v) =>
val gap = desiredPercentages(k) - v
(k -> gap)
}
val maxUnder = gaps.values.toList.sortWith(_ > _).head
//println("Max gap is " + maxUnder)
val gapsForMaxUnder = gaps.mapValues{v => Math.abs(v - maxUnder) < Double.Epsilon }
val keysByHasMaxUnder = gapsForMaxUnder.map(_.swap)
keysByHasMaxUnder(true)
}
/**
Stream of most-fair next element
*/
def proportionalStream[T](proportions : Map[T, Double], toDate : Map[T, Double]) : Stream[T] = {
val nextS = next(proportions, toDate)
val tailToDate = toDate + (nextS -> (toDate(nextS) + 1.0))
Stream.cons(
nextS,
proportionalStream(proportions, tailToDate)
)
}
That when used, e.g., :
val ss : Map[String,Double] = Map("A"->42, "B"->32, "C"->26)
val none : Map[String,Double] = ss.mapValues(_ => 0.0)
val mySequence = (proportionalStream(ss, none) take 100).toList
println("Desired : " + ss)
println("Achieved : " + mySequence.groupBy(identity).mapValues(_.size))
mySequence.map(s => print(s))
println
produces :
Desired : Map(A -> 42.0, B -> 32.0, C -> 26.0)
Achieved : Map(C -> 26, A -> 42, B -> 32)
ABCABCABACBACABACBABACABCABACBACABABCABACABCABACBA
CABABCABACBACABACBABACABCABACBACABABCABACABCABACBA
For a deterministic approach, the most obvious solution would probably be this:
Keep track of the number of occurrences of each item in the sequence so far.
For the next item, choose that item for which the difference between intended and actual count (or proportion, if you prefer that) is maximal, but only if the intended count (resp. proportion) is greater than the actual one.
If there is a tie, break it in an arbitrary but deterministic way, e.g. choosing the alphabetically lowest item.
This approach would ensure an optimal adherence to the prescribed ratio for every prefix of the infinite sequence generated in this way.
Quick & dirty python proof of concept (don't expect any of the variable “names” to make any sense):
import sys
p = [0.42, 0.32, 0.26]
c = [0, 0, 0]
a = ['A', 'B', 'C']
n = 0
while n < 70*5:
n += 1
x = 0
s = n*p[0] - c[0]
for i in [1, 2]:
si = n*p[i] - c[i]
if si > s:
x = i
s = si
sys.stdout.write(a[x])
if n % 70 == 0:
sys.stdout.write('\n')
c[x] += 1
Generates
ABCABCABACABACBABCAABCABACBACABACBABCABACABACBACBAABCABCABACABACBABCAB
ACABACBACABACBABCABACABACBACBAABCABCABACABACBABCAABCABACBACABACBABCABA
CABACBACBAABCABCABACABACBABCABACABACBACBAACBABCABACABACBACBAABCABCABAC
ABACBABCABACABACBACBAACBABCABACABACBACBAABCABCABACABACBABCABACABACBACB
AACBABCABACABACBACBAABCABCABACABACBABCAABCABACBACBAACBABCABACABACBACBA
For every item of the sequence, compute a (pseudo-)random number r equidistributed between 0 (inclusive) and 100 (exclusive).
If 0 ≤ r < 42, take A
If 42 ≤ r < (42+32), take B
If (42+32) ≤ r < (42+32+26)=100, take C
The number of each entry in your subset is going to be the same as in your map, but with a scaling factor applied.
The scaling factor is n/100.
So if n was 50, you would have { Ax21, Bx16, Cx13 }.
Randomize the order to your liking.
The simplest "deterministic" [in terms of #elements of each category] solution [IMO] will be: add elements in predefined order, and then shuffle the resulting list.
First, add map(x)/100 * n elements from each element x chose how you handle integer arithmetics to avoid off by one element], and then shuffle the resulting list.
Shuffling a list is simple with fisher-yates shuffle, which is implemented in most languages: for example java has Collections.shuffle(), and C++ has random_shuffle()
In java, it will be as simple as:
int N = 107;
List<String> res = new ArrayList<String>();
for (Entry<String,Integer> e : map.entrySet()) { //map is predefined Map<String,Integer> for frequencies
for (int i = 0; i < Math.round(e.getValue()/100.0 * N); i++) {
res.add(e.getKey());
}
}
Collections.shuffle(res);
This is nondeterministic, but gives a distribution of values close to MvG's. It suffers from the problem that it could give AAA right at the start. I post it here for completeness' sake given how it proves my dissent with MvG was misplaced (and I don't expect any upvotes).
Now, if someone has an idea for an expand function that is deterministic and won't just duplicate MvG's method (rendering the calc function useless), I'm all ears!
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>ErikE's answer</title>
</head>
<body>
<div id="output"></div>
<script type="text/javascript">
if (!Array.each) {
Array.prototype.each = function(callback) {
var i, l = this.length;
for (i = 0; i < l; i += 1) {
callback(i, this[i]);
}
};
}
if (!Array.prototype.sum) {
Array.prototype.sum = function() {
var sum = 0;
this.each(function(i, val) {
sum += val;
});
return sum;
};
}
function expand(counts) {
var
result = "",
charlist = [],
l,
index;
counts.each(function(i, val) {
char = String.fromCharCode(i + 65);
for ( ; val > 0; val -= 1) {
charlist.push(char);
}
});
l = charlist.length;
for ( ; l > 0; l -= 1) {
index = Math.floor(Math.random() * l);
result += charlist[index];
charlist.splice(index, 1);
}
return result;
}
function calc(n, proportions) {
var percents = [],
counts = [],
errors = [],
fnmap = [],
errorSum,
worstIndex;
fnmap[1] = "min";
fnmap[-1] = "max";
proportions.each(function(i, val) {
percents[i] = val / proportions.sum() * n;
counts[i] = Math.round(percents[i]);
errors[i] = counts[i] - percents[i];
});
errorSum = counts.sum() - n;
while (errorSum != 0) {
adjust = errorSum < 0 ? 1 : -1;
worstIndex = errors.indexOf(Math[fnmap[adjust]].apply(0, errors));
counts[worstIndex] += adjust;
errors[worstIndex] = counts[worstIndex] - percents[worstIndex];
errorSum += adjust;
}
return expand(counts);
}
document.body.onload = function() {
document.getElementById('output').innerHTML = calc(99, [25.1, 24.9, 25.9, 24.1]);
};
</script>
</body>
</html>

Scheduling algorithm for a round-robin tournament?

I recently did studying stuff and meet up with Donald Knuth. But i didn't found the right algorithm to my problem.
The Problem We have a league with n players. every week they have a match with one other. in n-1 weeks every team fought against each other. there are n/2 matches a day. but one team can only fight once in a week. if we generate an (n/k) combination we get all of the combinations... (assuming k = 2) but i need to bring them in the right order.
My first suggestion was... not the best one. i just made an array, and then let the computer try if he finds the right way. if not, go back to the start, shuffle the array and do it again, well, i programmed it in PHP (n=8) and what comes out works, but take many time, and for n=16 it gives me a timeout as well.
So i thought if maybe we find an algorithm, or anybody knows a book which covers this problem.
And here's my code:
http://pastebin.com/Rfm4TquY
The classic algorithm works like this:
Number the teams 1..n. (Here I'll take n=8.)
Write all the teams in two rows.
1 2 3 4
8 7 6 5
The columns show which teams will play in that round (1 vs 8, 2 vs 7, ...).
Now, keep 1 fixed, but rotate all the other teams. In week 2, you get
1 8 2 3
7 6 5 4
and in week 3, you get
1 7 8 2
6 5 4 3
This continues through week n-1, in this case,
1 3 4 5
2 8 7 6
If n is odd, do the same thing but add a dummy team. Whoever is matched against the dummy team gets a bye that week.
Here is the code for it in JavaScript.
function makeRoundRobinPairings(players) {
if (players.length % 2 == 1) {
players.push(null);
}
const playerCount = players.length;
const rounds = playerCount - 1;
const half = playerCount / 2;
const tournamentPairings = [];
const playerIndexes = players.map((_, i) => i).slice(1);
for (let round = 0; round < rounds; round++) {
const roundPairings = [];
const newPlayerIndexes = [0].concat(playerIndexes);
const firstHalf = newPlayerIndexes.slice(0, half);
const secondHalf = newPlayerIndexes.slice(half, playerCount).reverse();
for (let i = 0; i < firstHalf.length; i++) {
roundPairings.push({
white: players[firstHalf[i]],
black: players[secondHalf[i]],
});
}
// rotating the array
playerIndexes.push(playerIndexes.shift());
tournamentPairings.push(roundPairings);
}
return tournamentPairings;
}
UPDATED:
Fixed a bug reported in the comments
I made this code, regarding the Okasaki explanation
const roundRobin = (participants) => {
const tournament = [];
const half = Math.ceil(participants.length / 2);
const groupA = participants.slice(0, half);
const groupB = participants.slice(half, participants.length);
groupB.reverse();
tournament.push(getRound(groupA, groupB));
for(let i=1; i < participants.length - 1; i ++) {
groupA.splice(1, 0, groupB.shift());
groupB.push(groupA.pop())
tournament.push(getRound(groupA, groupB));
}
console.log(tournament)
console.log("Number of Rounds:", tournament.length)
return tournament;
}
const getRound = (groupA, groupB) => {
const total = [];
groupA.forEach((p, i) => {
total.push([groupA[i], groupB[i]]);
});
return total;
}
roundRobin([1,2,3,4,5,6,7])
P.S.: I put an example with an odd amount, so there is a team doesn't play every round, dueling with undefined, you can customize it the way you want
I made an updated solution for this with reusable functions (inspired by varun):
const testData = [
"Red",
"Orange",
"Yellow",
"Green",
"Blue",
"Indigo",
"Violet",
];
const matchParticipants = (participants) => {
const p = Array.from(participants);
if (p % 2 == 1) {
p.push(null);
}
const pairings = [];
while (p.length != 0) {
participantA = p.shift();
participantB = p.pop();
if (participantA != undefined && participantB != undefined) {
pairings.push([participantA, participantB]);
}
}
return pairings;
};
const rotateArray = (array) => {
const p = Array.from(array);
const firstElement = p.shift();
const lastElement = p.pop();
return [firstElement, lastElement, ...p];
};
const generateTournament = (participants) => {
const tournamentRounds = [];
const rounds = Math.ceil(participants.length / 2);
let p = Array.from(participants);
for (let i = 0; i < rounds; i++) {
tournamentRounds.push(matchParticipants(p));
p = rotateArray(p);
}
return tournamentRounds;
};
console.log(generateTournament(testData));
here is the code in python for those interested :
def makePairing(inputList):
if len(inputList) % 2 == 1:
inputList.append("No Opponent")
pairings = list()
for round in range(len(inputList) - 1):
round_pairings = list()
first_half = inputList[:int(len(inputList)/2)]
second_half = list(reversed(inputList[int(len(inputList)/2):]))
for element in range(len(first_half)):
round_pairings.append((first_half[element], second_half[element]))
pairings.append(round_pairings)
inputList = inputList[0:1] + inputList[2:] + inputList[1:2]
return pairings

Resources