MergeMap operator does not guarantee order. Is there a better way? - rxjs

Consider the following:
import { timer, of, Subject } from 'rxjs';
import { map, mergeMap } from 'rxjs/operators';
(function(){
var sub$ = new Subject<number>();
var complete = false;
var out$ = sub$.pipe(
mergeMap(n => {
complete = n === 7;
if (complete) {
return of(n);
} else {
return timer(1000).pipe(map(() => n));
}
})
);
out$.subscribe({
next: (n) => console.log(n)
});
for (var i = 1; i < 8; i++) {
sub$.next(i);
}
sub$.complete();
})();
Even though the values broadcasted by the subject, in order, are 1, 2, 3, 4, 5, 6, 7, the output of the above code is:
7
1
2
3
4
5
6
How can this pipe be organized differently to assure order despite internal delays?

You can use concatMap instead of mergeMap to achieve this https://rxjs.dev/api/index/function/concatMap

use timer(1000).pipe(map(() => n)) instead of of(n).
or use endWith operator

Related

model.fit() never ends or shows me the loss

I'm trying to train a model and never pass the fit().
In the console doesn't show the loss result, it gets stuck there.
Already changed the async to a promise, but it's the same.
To see the entire code, click here!
function train() {
trainModel().then(result => {
console.log(result.history.loss[0]);
setTimeout(train, 100);
});
}
// entrena modelo~ params = train_xs(input) y train_ys(output)
async function trainModel() {
//Create the input data
for (let i = 0; i < 5; i++) {
train_xs = tf.tensor2d(ins.pixels[i], [28, 28], 'int32');
train_ys = tf.tensor2d(outs.coords[i], [3, 2], 'int32');
const h = await model.fit(train_xs, train_ys, {
epochs: 1
});
console.log("Loss after Epoch " + i + " : " + h.history.loss[0]);
}
console.log('end fitness model');
}
//never shows end fitness model
no error messages, the console keeps just clean
There are a couple of issues (the console is clean because it was not logging out the errors):
the shape of xs and ys does not match the input and output of the model.ins.pixels[i]
xs and ys should have the same batch size. Since in all iteration of the for loop, only one feature and one label is used, therefore the batchsize is 1.
Here is a fix of the model
let model;
let xs;
let train_xs;
let train_ys;
let inAndOut;
let resolution = 20;
let cols;
let rows;
var ins;
var outs;
function setup() {
createCanvas(400, 400);
/// visualization
ins = new Inputs13(); // ins.pixels;
outs = new Outputs13(); // outs.coords;
inAndOut = new InputsAndOutputsToTest();
///crear modelo
model = tf.sequential();
let hidden = tf.layers.dense({
inputShape: [784],
units: 28,
activation: 'sigmoid'
});
let output = tf.layers.dense({
units: 6,
activation: 'sigmoid'
});
model.add(hidden);
model.add(output);
const optimizer = tf.train.adam(0.1);
model.compile({
optimizer: optimizer,
loss: 'meanSquaredError'
})
xs = tf.tensor2d(inAndOut.pixelsToTest[0],[28,28]);
//console.log('xs');
//console.log(xs);
//xs.print();
//entrena modelo
setTimeout(train, 10);
}
//promesa, llama a entrenar modelo y muestra de losss
function train() {
console.log("im in train!");
trainModel().then(result => {
console.log(result.history.loss[0]);
setTimeout(train, 100);
});
}
// entrena modelo~ params = train_xs(input) y train_ys(output)
async function trainModel() {
let h;
//Create the input data
for (let i = 0; i < 5; i++) {
train_xs = tf.tensor(ins.pixels[i], [1, 784]); //[tens], [shape]
console.log('xs.shape', train_xs.shape)
train_ys = tf.tensor(outs.coords[i]).reshape([1, 6]);
console.log('ys.shape', train_ys.shape)
/* console.log('train_xs');
train_xs.print();
console.log("train_ys");
train_ys.print();*/
h = await model.fit(train_xs, train_ys, {
// shuffle: true,
epochs: 1
});
console.log("Loss after Epoch " + i + " : " + h.history.loss[0]);
}
console.log('end fitness model');
return h;
}
//muestra visual!
function draw() {
background(220);
//Get the predictions params xs = inputs para pruebas
tf.tidy(() => {
let ys = model.predict(xs);
//console.log("ys");
//console.log(ys);
let y_values = ys.dataSync();
// console.log("y_values");
// console.log(y_values);
});
}
However, it is possible to use all the 13 features and 13 labels all at once. The for loop will no longer be useful.
train_xs = tf.tensor(ins.pixels, [13, 784]);
console.log('xs.shape', train_xs.shape)
train_ys = tf.tensor(outs.coords).reshape([13, 6]);

TPL Dataflow never completes when using a predicate

I have the following TPL Dataflow that never completes when using a predicate to filter the items passed from the TransformBlock to the ActionBlock.
If the predicate returns false for any of the items, then the dataflow hangs.
Please can someone offer some insight as to what's happening and how to resolve this?
// define blocks
var getBlock = new TransformBlock<int, int>(i =>
{
Console.WriteLine($"getBlock: {i}");
return ++i;
});
var writeBlock = new ActionBlock<int>(i =>
{
Console.WriteLine($"writeBlock: {i}");
});
// link blocks
getBlock.LinkTo(writeBlock, new DataflowLinkOptions
{
PropagateCompletion = true
}, i => i == 12); // <-- this predicate prevents the completion of writeBlock
// push to block
var items = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
foreach (var i in items)
{
getBlock.Post(i);
}
// wait for all operations to complete
getBlock.Complete();
await writeBlock.Completion; // <-- application hangs here
The getBlock is not completing because the items posted to it have nowhere to go. If you have a predicate add a null target so that any items that don't match have a place to exit the pipeline.
getBlock.LinkTo(writeBlock, new DataflowLinkOptions
{
PropagateCompletion = true
}, i => i == 12)
getBlock.LinkTo(DataflowBlock.NullTarget<int>());

TypeScript for ... of with index / key?

As described here TypeScript introduces a foreach loop:
var someArray = [9, 2, 5];
for (var item of someArray) {
console.log(item); // 9,2,5
}
But isn't there any index/key? I would expect something like:
for (var item, key of someArray) { ... }
.forEach already has this ability:
const someArray = [9, 2, 5];
someArray.forEach((value, index) => {
console.log(index); // 0, 1, 2
console.log(value); // 9, 2, 5
});
But if you want the abilities of for...of, then you can map the array to the index and value:
for (const { index, value } of someArray.map((value, index) => ({ index, value }))) {
console.log(index); // 0, 1, 2
console.log(value); // 9, 2, 5
}
That's a little long, so it may help to put it in a reusable function:
function toEntries<T>(a: T[]) {
return a.map((value, index) => [index, value] as const);
}
for (const [index, value] of toEntries(someArray)) {
// ..etc..
}
Iterable Version
This will work when targeting ES3 or ES5 if you compile with the --downlevelIteration compiler option.
function* toEntries<T>(values: T[] | IterableIterator<T>) {
let index = 0;
for (const value of values) {
yield [index, value] as const;
index++;
}
}
Array.prototype.entries() - ES6+
If you are able to target ES6+ environments then you can use the .entries() method as outlined in Arnavion's answer.
See: Array.prototype.entries()
for (const [key, item] of someArray.entries()) { ... }
In TS this requires targeting ES2015 since it requires the runtime to support iterators, which ES5 runtimes don't. You can of course use something like Babel to make the output work on ES5 runtimes.
"Old school javascript" to the rescue (for those who aren't familiar/in love of functional programming)
for (let i = 0; i < someArray.length ; i++) {
let item = someArray[i];
}
You can use the for..in TypeScript operator to access the index when dealing with collections.
var test = [7,8,9];
for (var i in test) {
console.log(i + ': ' + test[i]);
}
Output:
0: 7
1: 8
2: 9
See Demo
Or another old school solution:
var someArray = [9, 2, 5];
let i = 0;
for (var item of someArray) {
console.log(item); // 9,2,5
i++;
}

What is d3.geo.pipeline?

If you follow Mike Bostock's bl.ocks, you know for the last 8 months d3.geo.pipeline() has been a frequent component to his projects.
But what's it do?
You see him set pipelines up like this:
var sketch = d3.geo.pipeline()
.source(d3.geo.jsonSource)
.pipe(resample, .020)
.pipe(jitter, .004)
.pipe(smooth, .005)
.sink(d3.geo.jsonSink);
via
There is no documentation in the d3.geo wiki.
Some beautified JS in the unreleased D3 used in the code example reveals this function:
lo.geo.pipeline = function() {
var n = [];
return {
source: function() {
return n[0] = arguments, this
},
pipe: function() {
return n.push(arguments), this
},
sink: function() {
for (var t, e = arguments[0].apply(null, [].slice.call(arguments, 1)), r = e; t = n.pop();) {
var u = [].slice.call(t, 1);
u.push(e), e = t[0].apply(null, u)
}
return function() {
return e.apply(this, arguments), r.value && r.value()
}
}
}
It also appears in these bl.ocks:
Back-facing Hemisphere
Multiple rotations
Satellite
I'm not familiar with d3.js, but I looked at its source code and found that this feature is located in branch graphics-pipeline.
For example you can find related code here: https://github.com/mbostock/d3/commit/a3f2adab7f85e2a0c82288ead88c1e484c9e3ea3
Small code snippet to illustrate how it works:
var pipeline = function () {
var pipes = [];
return {
source: function () {
pipes[0] = arguments;
return this;
},
pipe: function () {
pipes.push(arguments);
return this;
},
sink: function () {
var sink = arguments[0].apply(null, [].slice.call(arguments, 1)),
pipe;
while (pipe = pipes.pop()) {
var args = [].slice.call(pipe, 1);
args.push(sink);
sink = pipe[0].apply(null, args);
}
return sink;
}
};
};
var log = document.getElementById('log');
function f() {
var argsAsString = Array.prototype.join.call(arguments, ', ');
var resultName = 'r' + f.callCounter++;
log.innerHTML += resultName + ' = f(' + argsAsString + ')<br>';
return resultName;
}
f.callCounter = 1;
pipeline().
source(f, 'a', 1).
pipe(f, 'b', 2).
pipe(f, 'c', 3).
sink(f, 'd', 4);
<div id="log"></div>
Few comments about this feature:
Methods source and pipe work with the same private property pipes. The difference only is that source set initial value for pipes (pipes[0]), when each call to pipe pushes new pipe into collection.
Previous fact gives us knowledge about d3.geo.jsonSource internal structure. It should be similar to arguments passed to pipe: first argument is something callable (function), rest of arguments - parameters.
Assume that arguments = [f, a, b, c]. Then JavaScript pattern arguments[0].apply(null, [].slice.call(arguments, 1)) means: f(a, b, c). You can see several places of its usage in sink implementation.
Regarding practical usage.
We can use it if we need to "chain" (or "pipe") data processing. For example if we have such code:
function f(a, b, previousResult)
{
return a * b + (previousResult || 0);
}
var p = pipeline().
source(f, 1, 1).
pipe(f, 2, 10).
pipe(f, 3, 100).
sink(f, 4, 1000);
Then the result (value of p) will be 4321.
In this particular case we need to clarify what is d3.geo.jsonSink and d3.geo.jsonSource, but I hope that I helped you to see the meaning of pipeline function.

Linq GroupBy without merging groupings with the same key if they are separated by other key

I'd like to archieve behaviour similar to Pythons' groupby.
[1, 1, 2, 1].GroupBy() => [[1, 1], [2], [1]]
I think this is what you're looking for:
var data = new int[] { 1, 1, 2, 1 };
var results = Enumerable.Range(0, data.Count ())
.Where (i => i == 0 || data.ElementAt(i - 1) != data.ElementAt(i))
.Select (i => new
{
//Key = data.ElementAt(i),
Group = Enumerable.Repeat(
data.ElementAt(i),
data.Skip(i).TakeWhile (d => d == data.ElementAt(i)).Count ())
}
);
Here's an example of it running and the results: http://ideone.com/NJGQB
Here's a lazy, generic extension method that does what you want.
Code:
public static IEnumerable<IEnumerable<T>> MyGroupBy<T>(this IEnumerable<T> source)
{
using(var enumerator = source.GetEnumerator())
{
var currentgroup = new List<T>();
while (enumerator.MoveNext())
{
if (!currentgroup.Any() || currentgroup[0].Equals(enumerator.Current))
currentgroup.Add(enumerator.Current);
else
{
yield return currentgroup.AsReadOnly();
currentgroup = new List<T>() { enumerator.Current };
}
}
yield return currentgroup.AsReadOnly();
}
}
Test:
void Main()
{
var data = new int[] { 1, 1, 2, 1 };
foreach(var g in data.MyGroupBy())
Console.WriteLine(String.Join(", ", g));
}
Output:
1, 1
2
1

Resources