Recognize a sequence of elements with minimum and maximum duration - system.reactive

Given a IObservable<bool>,and two TimeSpan thresholds, minDuration and maxDuration, I need to transform the sequence so that:
A sequence of true values that spans a time between minDuration and maxDuration transforms to "x"
A sequence of true values that spans more than maxDuration transforms to "y"; "y" should be emitted after maxDuration time passed since the first true value was emitted
To make things clearer, assuming minDuration = 3, maxDuration = 6, and assuming items are emitted at the speed of one per second:
fffttffttfftttttttttttffftttffffttttf
------------------y--------x-------x-
My guess is that I need to implement a custom operator, but as a RX newbie I don't know how, and I'm having a hard time finding examples that go beyond composing the existing operator with an extension method.
Links to tutorials and examples on implementing custom operators are welcome.

If I understand this right:
When you get consecutive true values for more than the minDuration but less than the maxDuration, you want to emit x.
When you get consecutive true values for more than the maxDuration, you want to emit y.
In any other scenario, emit nothing.
If that's the case, your marble diagram should look more like this:
fffttffttfftttttttttttffftttffffttttf
1234567 123 1234 (numbers for counting guidance)
-----------------y----------x-------x (mine)
------------------y--------x-------x- (yours)
The x can only come out on the false. You can't emit it on the true, because you don't know what a future value will be! No custom operator can solve that for you.
The rest could be solved with this:
var values = new List<bool> // matches fffttffttfftttttttttttffftttffffttttf
{
false,
false,
false,
true,
true,
false,
false,
true,
true,
false,
false,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
false,
false,
false,
true,
true,
true,
false,
false,
false,
false,
true,
true,
true,
true,
false,
};
var original = Observable.Interval(TimeSpan.FromSeconds(1))
.Where(i => i < values.Count)
.Select(i => values[(int)i]);
TimeSpan minDuration = TimeSpan.FromSeconds(3);
TimeSpan maxDuration = TimeSpan.FromSeconds(6);
var trueWindows = original
.TimeInterval()
.Scan(TimeSpan.Zero, (a, t) => t.Value
? a + t.Interval
: TimeSpan.Zero);
var coupledWindows = trueWindows.Scan(new {Previous = TimeSpan.Zero, Current = TimeSpan.Zero},
(o, t) => new {Previous = o.Current, Current = t})
.Publish()
.RefCount();
var xS = coupledWindows.Where(o => o.Previous < maxDuration && o.Previous >= minDuration && o.Current == TimeSpan.Zero).Select(t => "X");
var yS = coupledWindows.Where(o => o.Current >= maxDuration && o.Previous < maxDuration).Select(t => "Y");
As for tutorials, the best resource is http://introtorx.com/. Another somewhat good one is http://rxmarbles.com, though it uses non-.NET function names.

Related

How do I calculate a percentage based on a List of booleans?

I have a list of booleans here:
List<bool> list = [false, false, false, false];
Using that as an example, if I were to find the percentage of values that are true, it would be 0%.
Using another example:
List<bool> list = [true, true, false, false];
The percentage of values that are true would be 50%.
I was able to find something similar to what I need here: Flutter - Count List with bool which works.
However, the calculations to percentages are a little lost on me.
It's basically just the number of true elements divided by the total number of elements.
void main() {
List<bool> list = [true, true, false, false];
double percent = list.where((v) => v).length / list.length;
String percentString = '${(percent * 100).round()}%';
print(percent);
print(percentString);
}
You should probably do something like :
int trueValues = list.fold(0, (acc, val) => val ? ++acc : acc);
double truePercent = trueValues / list.length;
count the number of 'true' value and divide it by the list size (total number of values)

IColumn in DetailsList: how to set column width at mount

How can you set the columns of a DetailsList to a specific width at mount?k
I wanted to save the column widths every time they are saved and restore them when the component remounts, i.e., revisits the view where the list is.
return [...staticColumns, ...visibleColumns.map(c => {
return {
key: c.Name,
name: c.Title,
fieldName: c.Name,
minWidth: 100,
isResizable: true,
isCollapsible: true,
onRender: (item: any, index: number, column: IColumn) => {
column.minWidth = 20;
return <span>{item[c.Name]}</span>;
},
} as IColumn;
})];
First set initial minWidth: 100, then change to 20 in the onRender callback. It's work for me
Found this. Guess you can't easily.
https://github.com/microsoft/fluentui/issues/9287

Dart Not understanding how forEach is supposed to work

I simply wanted to use forEach to set all values in a List to false, but I don't understand why it doesn't work. I created a filled list with fixed length like this:
List<bool> myList = List<bool>.filled(6, false);
Then I set one value to true:
setState(() => myList[3] = true);
Then I try to reset all values to false again, but as you can see from the print output it does not work:
setState(() {
myList.forEach((val) => val = false);
print(myList);
});
I/flutter (29049): [false, false, false, true, false, false]
You can check the answer why you can't update the values inside forEach here: List.forEach unable to modify element?
Dart does not have variable references, all elements are passed as a reference to an object, not to a variable. (In other words, Dart is purely "call-by-sharing" like both Java and JavaScript).
That means that the e parameter to the forEach callback is just a normal local variable, and assigning to it has no effect outside the callback. The same goes for iterators: they return the value in the iterable, but it has no reference back to the iterable after that.
You can do what you want using filled method like you used to create the list.
setState(() {
myList = List<bool>.filled(myList.length, false);
print(myList);
});
forEach element can not modify the actual element in the list. Assume this code:
var list = [false, true, false, false];
list.forEach((item) {
item = false;
});
print("List: $list");
The output is still:
List: [false, true, false, false]
So what you can do is using an indexed for:
for (int i=0; i < list.length; i++) {
list[i] = false;
}
Or map it and reassign it:
var list = [true, true, false];
list = list.map((item) {
return false;
}).toList();
You'll get:
List: [false, false, false, false]
As pointed out above, the forEach function does not give you write access to the list, it just provides you with the value. Changing the parameter of the forEach callback function has no effect outside of that function call, it's just changing a local variable.
To change the list, you must store to its element slots directly. Either:
for (int i = 0; i < list.length; i++) list[i] = false;
or
list.fillRange(0, list.length, false);
should do the job.

ChartJS / MomentJS - Unable to remove deprecation warning. Graph not showing in firefox/opera

so browsers throw
warning about using momentJS incorrectly.
Deprecation warning: value provided is not in a recognized ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.
Arguments:
[0] _isAMomentObject: true, _isUTC: false, _useUTC: false, _l: undefined, _i: 12.30, _f: false, _strict: undefined, _locale: [object Object]
Error
So i looked at my code
data: {
labels: ['01.01', '02.01', '03.01', '04.01', '05.01', '06.01', '07.01', '08.01', '09.01', '10.01', '11.01', '12.01'],
datasets: createChatterData(data, this)
},
And read that I should provide a format when dealing with non iso strings.
labels: [moment('01.01', 'MM.DD'), moment('02.01', 'MM.DD'), ...];
Ok that removed first deprecation.
But my datasets data also contains of date
dataset.data.pushObject({
x: moment(datum).format('MM.DD'),
y: parseInt(moment(datum).format('YYYY'))
});
So I tried different variations to that (premodified ambigious datetime)
x: moment(date, 'YYYY.MM.DD').format('MM.DD')
and
x: moment(date, 'MM.DD')
But my graph doesnt map correctly anymore.
Example of codepen chart working in chrome: http://codepen.io/kristjanrein/pen/wJrQLE
Does not display in firefox/opera
I see a couple of issues here.
1) Since you want your X axis to be a time scale, then you should leave your X data value as a moment object. Your current implementation is creating a moment object from a date string and then formatting it back to a string. When you do this, chart.js then takes the string and tries to create a moment object internally when it builds the chart.
Therefore, It is best to keep the data as either a Date or Moment object and use the time scale configuration properties to determine how the data is displayed on the chart. This prevents chart.js from having to construct the moment object and guess at the string format.
2) You are using the pre-2.0 way to create a chart when you use Chart.Scatter. Instead you should use the new style (new Chart()) and pass in a type property.
Here is a modified version of you code that results in no browser warnings and works in Chrome and Firefox (I did not test in Opera).
var getData = function() {
var dummyDataset = [
'2007-11-09T00:00:00.000Z',
'2006-08-04T00:00:00.000Z',
'2006-08-06T00:00:00.000Z',
'2008-01-10T00:00:00.000Z'
];
return dummyDataset.map(function(datum) {
var myMoment = moment(datum);
return {
x: myMoment,
y: parseInt(myMoment.format('YYYY')),
};
});
};
var ctx = document.getElementById("chart1").getContext("2d");
var myScatter = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: "My First dataset",
borderColor: 'rgb(255, 99, 132)',
fill: false,
pointRadius: 4,
pointHoverRadius: 8,
showLine: false,
data: getData()
}]
},
options: {
responsive: true,
title: {
display: true,
text: 'Random Data'
},
legend: {
display: true,
labels: {
fontSize: 10,
boxWidth: 20
}
},
elements: {
point: {
pointStyle: 'rect'
}
},
hover: {
mode: 'nearest'
},
scales: {
xAxes: [{
type: 'time',
position: 'bottom',
scaleLabel: {
display: true,
labelString: 'Months'
},
time: {
unit: 'month',
displayFormats: {
month: 'MM'
},
}
}],
yAxes: [ {
type: 'linear',
ticks: {
min: 2005,
max: 2015,
stepSize: 1
},
scaleLabel: {
display: true,
labelString: 'Year'
}
}]
}
}
});
You can see it in action at this forked codepen.
One other thing to keep in mind is that because your data spans multiple years, you will see duplicate months on the X axis. Remember, a time scale is used to plot dates so even if you only display the months, a data point with the same month but with different years will not be plotted at the same location.
If you are actually only wanting to show month string/number values in the X axis, then you should not use the time scale at all and use the linear scale instead. Then when you build your data values, you would extract the month from the data (the same way you are already doing for your Y value).
var getData = function() {
var dummyDataset = [
'2007-11-09T00:00:00.000Z',
'2006-08-04T00:00:00.000Z',
'2006-08-06T00:00:00.000Z',
'2008-01-10T00:00:00.000Z'
];
return dummyDataset.map(function(datum) {
var myMoment = moment(datum);
return {
x: parseInt(myMoment.format('MM')),
y: parseInt(myMoment.format('YYYY')),
};
});
};
So in addition to jordan's answer
I changed my labels and x axis from
['01.01', '02.01', ...] to [1,2,...]
and
from type: 'time' to type: 'linear'
And to make it map not only by month but also by day. I had to make date objects to correct floats. 05.20 to 5.66
const date = datum.key;
const day = parseInt(moment(date).format('DD')) / 30 * 100;
const fullDate = parseFloat(moment(date).format('MM') + '.' + Math.round(day))
// 05.10 would be 5.3 (10 of 30 is 33%)
{
x: fullDate,
y: parseInt(moment(date).format('YYYY'))
date: date, // for tooltip
count: count // for tooltip
}
And i also had to make corrections to my tooltips
callbacks: {
title: function([tooltipItem], data) {
const tooltipInfo = getTooltip(tooltipItem, data.datasets);
return tooltipInfo.date;
},
label: function(tooltipItem, data) {
const tooltipInfo = getTooltip(tooltipItem, data.datasets);
return i18n.t('chart.count') + ': ' + tooltipInfo.count;
},
}
corresponding tooltip dataset
function getTooltip(tooltipItem, datasets) {
return datasets[tooltipItem.datasetIndex].data.find(datum => {
return datum.x === tooltipItem.xLabel && datum.y === tooltipItem.yLabel;
});
}

CodeMirror, how fold all the code

With code mirror, we can fold the code.
I would like fold all the code with brace. I found this method
How i can fold the entire code, this is my HTML script :
window.onload = function() {
var te = document.getElementById("code");
var sc = document.getElementById("script");
var te_clike = document.getElementById("code-clike");
window.editor_clike = CodeMirror.fromTextArea(te_clike, {
mode: "text/x-csharp",
lineNumbers: true,
extraKeys: {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }},
foldGutter: true,
readOnly: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
});
};
Thanks you for your help...
There is also a function designed to do it for you, for example:
editor.setOption("extraKeys", {
"Ctrl-Y": cm => CodeMirror.commands.foldAll(cm),
"Ctrl-I": cm => CodeMirror.commands.unfoldAll(cm),
})
CodeMirror has a foldCode plugin which enables a foldCode() method. Reference can be found here: https://codemirror.net/doc/manual.html#addon_foldcode
You can then loop through all the lines and call that function to fold the code at that particular line, like the solution here: https://groups.google.com/forum/#!msg/CodeMirror/u3IYL-5g0t4/lmK8XuTxbdQJ
cm.operation(function() {
for (var l = cm.firstLine(); l <= cm.lastLine(); ++l)
cm.foldCode({line: l, ch: 0}, null, "fold");
});
where cm is the CodeMirror instance.