The following example
var subject = new Subject<int>();
var otherSubject = new Subject<Unit>();
subject
.SkipUntil(otherSubject)
.Subscribe(Console.WriteLine, () => Console.WriteLine("Completed"));
subject.OnNext(1);
subject.OnNext(2);
subject.OnNext(3);
otherSubject.OnNext(Unit.Default);
subject.OnNext(4);
subject.OnNext(5);
subject.OnNext(6);
subject.OnNext(7);
subject.OnNext(8);
subject.OnCompleted();
produces the output
4
5
6
7
completed
Shouldn't it emit the value 8 also? Why is it not emitting the value 8?
...brought to you by the works on my machine certification program
Related
This question has been posted multiple times before but the answers no longer appear to match the method signatures available in the latest NuGet packages.
After spending days trying to piece together an answer from other places (1, 2, 3, 4, 5, 6, blogs and even Microsoft's official spiel et al...) this is as far as I've got, but it won't run:
#using NetTopologySuite
#using NetTopologySuite.Geometries
#using ProjNet
#using ProjNet.CoordinateSystems
#using MyApp.Helpers
#{
var windsorCastle = Geography.CreatePoint(51.483884, -0.604455);
var buckinghamPalace = Geography.CreatePoint(51.501576, -0.141208);
var csWgs84 = ProjNet.CoordinateSystems.GeographicCoordinateSystem.WGS84;
const string epsg27700 = #"
PROJCS[""OSGB36 / British National Grid"",
GEOGCS[""OSGB36"",
DATUM[""Ordnance_Survey_of_Great_Britain_1936"",
SPHEROID[""Airy 1830"",6377563.396,299.3249646],
EXTENSION[""PROJ4_GRIDS"",""OSTN15_NTv2_OSGBtoETRS.gsb""]],
PRIMEM[""Greenwich"",0,
AUTHORITY[""EPSG"",""8901""]],
UNIT[""degree"",0.0174532925199433,
AUTHORITY[""EPSG"",""9122""]],
AUTHORITY[""EPSG"",""4277""]],
PROJECTION[""Transverse_Mercator""],
PARAMETER[""latitude_of_origin"",49],
PARAMETER[""central_meridian"",-2],
PARAMETER[""scale_factor"",0.9996012717],
PARAMETER[""false_easting"",400000],
PARAMETER[""false_northing"",-100000],
UNIT[""metre"",1,
AUTHORITY[""EPSG"",""9001""]],
AXIS[""Easting"",EAST],
AXIS[""Northing"",NORTH],
AUTHORITY[""EPSG"",""27700""]]
"; // see http://epsg.io/27700
var cs27700 = (CoordinateSystem) ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(epsg27700);
var ctFactory = new ProjNet.CoordinateSystems.Transformations.CoordinateTransformationFactory();
var ct = ctFactory.CreateFromCoordinateSystems(csWgs84, cs27700);
var mt = ct.MathTransform;
var gf = new NetTopologySuite.Geometries.GeometryFactory(new PrecisionModel(PrecisionModels.Floating), 27700);
Console.WriteLine(windsorCastle.Distance(buckinghamPalace)); // Need metres, not degrees...
}
It blows up with this:
ArgumentException: Expecting (',') but got a '[' at line 6 column 47.
ProjNet.IO.CoordinateSystems.WktStreamTokenizer.ReadToken(string expectedToken)
ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.ReadGeographicCoordinateSystem(WktStreamTokenizer tokenizer)
ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.ReadProjectedCoordinateSystem(WktStreamTokenizer tokenizer)
ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.ReadCoordinateSystem(string coordinateSystem, WktStreamTokenizer tokenizer)
ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(string wkt)
MyApp.Pages.Pages_DistanceTest.ExecuteAsync() in DistanceTest.cshtml
var cs27700 = (CoordinateSystem) ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(epsg27700);
Any pointers on a simpler/correct solution would be welcome. I am new to the coordinate system so please be patient. Previously I've done this using TSQL with geography::Point().STDistance but the goal is an EF Core-only end result.
The CoordinateSystem WKT contains an EXTENSION for the GEOGCS|DATUM|SPHEROID definition.
You have to remove that, as ProjNET can't handle the EXTENSION tag.
For the coordinate transformation you need the following instructions:
var cwc = mt.Transform(windsorCastle.X, windsorCastle.Y);
var cbp = mt.Transform(buckinghamPalace.X, buckinghamPalace.Y);
Console.WriteLine(new Coordinate(cwc.x, cwc.y).Distance(new Coordinate(cbp.x, cbp.y)))
I have a piece of code meant to send recurring emails every 8 weeks to a list of addresses in a google sheet.
It does not seem to be sending out the mails.
I have tried various examples that I found online, with no success
ScriptApp.newTrigger("sendEmails")
.timeBased()
.onWeekDay(ScriptApp.WeekDay.THURSDAY)
.atHour(11)
.nearMinute(00)
.everyWeeks(8)
.create();
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("EMAILS")
var startRow = 2;
var numRows = sheet.getRange(1,4).getValue();
var dataRange = sheet.getRange(startRow, 1, numRows, 2)
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var emailAddress = row[0];
var message = row[1];
var subject = "mail subject here";
MailApp.sendEmail(emailAddress, subject, message);
}
}
when I run the script manually it seems to be working fine, so I guess the problem is in the new trigger part?
Problem:
Currently you have the code in place but it won't be used at all because it is not part of a function.
Requirement:
Trigger to run code every 8 weeks.
Solution:
Separate your trigger builder into a separate function.
Run the following script, it'll delete any triggers you may have accidentally set up and create a new one that should run as you're expecting.
function newTrigger() {
//clear all triggers
var tg = ScriptApp.getProjectTriggers();
if(tg.length>0){
for(i=0;i<tg.length;i++){
ScriptApp.deleteTrigger(tg[i]);
}
}
//build new trigger
ScriptApp.newTrigger("sendEmails")
.timeBased()
.onWeekDay(ScriptApp.WeekDay.THURSDAY)
.atHour(11)
.nearMinute(00)
.everyWeeks(8)
.create();
}
Notes:
You'll only need to run this function once to set up the trigger.
In your project's triggers, it'll show as "every week" but should actually only run every 8 weeks like we specified in the code using .everyWeeks(8).
References:
Installable Triggers
Class ClockTriggerBuilder
If I execute ONLY the 1st part of the following short script in Robo3T, I get the results printed to the screen.
If I execute it with 2nd part, I only get the standard answer "Script executed successfully, but there are no results to show".
How can I print intermediate results such as those expected for 2nd part ?
I precise that concession & mission are both similar JSON objects.
I spent hours on this simple question. Please help.
var mission = db.mission.find({"googleCalendarEventId":"QiVJdbL"});
while (mission.hasNext()) {
var record = mission.next();
print(record.googleCalendarEventId + "," + record.concessionIdSet[0])
};
// 2nd part //
var concession = db.cursor.findOne({"_id": ObjectId(mission.concessionIdSet[0])});
while (concession.hasNext()) {
var record = concession.next();
print(record)
};
Thanks again #stennie, for others who would have the same issues, here's one code that worked for me and the 2 errors I committed :
1/ difference between find (--> cursor) and findOne (--> 1 element)
2/ actions in the loop had to be nested inside the loop --> I used a function and it works:
const missions = db.mission.find({
$and:[{"concessionToInvoice": {$exists: false}},
{"concessionIdSet":{$size:1}}]
});
missions.forEach((mission) => {
const concession = getConcession(mission);
print(mission.googleCalendarEventId+"- invoice to :"+concession.name);
mission.concessionToInvoice = getConcessionToInvoice(concession); //another function
db.mission.save(mission);
});
function getConcession(mission){
const concession = db.concession.findOne({"_id": ObjectId(mission.concessionIdSet[0])});
return concession;
}
I've got a simple program here that displays the number of letters in various words. It works as expected.
static void Main(string[] args) {
var word = new Subject<string>();
var wordPub = word.Publish().RefCount();
var length = word.Select(i => i.Length);
var report =
wordPub
.GroupJoin(length,
s => wordPub,
s => Observable.Empty<int>(),
(w, a) => new { Word = w, Lengths = a })
.SelectMany(i => i.Lengths.Select(j => new { Word = i.Word, Length = j }));
report.Subscribe(i => Console.WriteLine($"{i.Word} {i.Length}"));
word.OnNext("Apple");
word.OnNext("Banana");
word.OnNext("Cat");
word.OnNext("Donkey");
word.OnNext("Elephant");
word.OnNext("Zebra");
Console.ReadLine();
}
And the output is:
Apple 5
Banana 6
Cat 3
Donkey 6
Elephant 8
Zebra 5
I used the Publish().RefCount() because "wordpub" is included in "report" twice. Without it, when a word is emitted first one part of the report would get notified by a callback, and then the other part of report would be notified, double the notifications. That is kindof what happens; the output ends up having 11 items rather than 6. At least that is what I think is going on. I think of using Publish().RefCount() in this situation as simultaneously updating both parts of the report.
However if I change the length function to ALSO use the published source like this:
var length = wordPub.Select(i => i.Length);
Then the output is this:
Apple 5
Apple 6
Banana 6
Cat 3
Banana 3
Cat 6
Donkey 6
Elephant 8
Donkey 8
Elephant 5
Zebra 5
Why can't the length function also use the same published source?
This was a great challenge to solve!
So subtle the conditions that this happens.
Apologies in advance for the long explanation, but bear with me!
TL;DR
Subscriptions to the published source are processed in order, but before any other subscription directly to the unpublished source. i.e. you can jump the queue!
With GroupJoin subscription order is important to determine when windows open and close.
My first concern would be that you are publish refcounting a subject.
This should be a no-op.
Subject<T> has no subscription cost.
So when you remove the Publish().RefCount() :
var word = new Subject<string>();
var wordPub = word;//.Publish().RefCount();
var length = word.Select(i => i.Length);
then you get the same issue.
So then I look to the GroupJoin (because my intuition suggests that Publish().Refcount() is a red herring).
For me, eyeballing this alone was too hard to rationalise, so I lean on a simple debugging too I have used dozens of times of the years - a Trace or Log extension method.
public interface ILogger
{
void Log(string input);
}
public class DumpLogger : ILogger
{
public void Log(string input)
{
//LinqPad `Dump()` extension method.
// Could use Console.Write instead.
input.Dump();
}
}
public static class ObservableLoggingExtensions
{
private static int _index = 0;
public static IObservable<T> Log<T>(this IObservable<T> source, ILogger logger, string name)
{
return Observable.Create<T>(o =>
{
var index = Interlocked.Increment(ref _index);
var label = $"{index:0000}{name}";
logger.Log($"{label}.Subscribe()");
var disposed = Disposable.Create(() => logger.Log($"{label}.Dispose()"));
var subscription = source
.Do(
x => logger.Log($"{label}.OnNext({x.ToString()})"),
ex => logger.Log($"{label}.OnError({ex})"),
() => logger.Log($"{label}.OnCompleted()")
)
.Subscribe(o);
return new CompositeDisposable(subscription, disposed);
});
}
}
When I add the logging to your provided code it looks like this:
var logger = new DumpLogger();
var word = new Subject<string>();
var wordPub = word.Publish().RefCount();
var length = word.Select(i => i.Length);
var report =
wordPub.Log(logger, "lhs")
.GroupJoin(word.Select(i => i.Length).Log(logger, "rhs"),
s => wordPub.Log(logger, "lhsDuration"),
s => Observable.Empty<int>().Log(logger, "rhsDuration"),
(w, a) => new { Word = w, Lengths = a })
.SelectMany(i => i.Lengths.Select(j => new { Word = i.Word, Length = j }));
report.Subscribe(i => ($"{i.Word} {i.Length}").Dump("OnNext"));
word.OnNext("Apple");
word.OnNext("Banana");
word.OnNext("Cat");
word.OnNext("Donkey");
word.OnNext("Elephant");
word.OnNext("Zebra");
This will then output in my log something like the following
Log with Publish().RefCount() used
0001lhs.Subscribe()
0002rhs.Subscribe()
0001lhs.OnNext(Apple)
0003lhsDuration.Subscribe()
0002rhs.OnNext(5)
0004rhsDuration.Subscribe()
0004rhsDuration.OnCompleted()
0004rhsDuration.Dispose()
OnNext
Apple 5
0001lhs.OnNext(Banana)
0005lhsDuration.Subscribe()
0003lhsDuration.OnNext(Banana)
0003lhsDuration.Dispose()
0002rhs.OnNext(6)
0006rhsDuration.Subscribe()
0006rhsDuration.OnCompleted()
0006rhsDuration.Dispose()
OnNext
Banana 6
...
However when I remove the usage Publish().RefCount() the new log output is as follows:
Log without only Subject
0001lhs.Subscribe()
0002rhs.Subscribe()
0001lhs.OnNext(Apple)
0003lhsDuration.Subscribe()
0002rhs.OnNext(5)
0004rhsDuration.Subscribe()
0004rhsDuration.OnCompleted()
0004rhsDuration.Dispose()
OnNext
Apple 5
0001lhs.OnNext(Banana)
0005lhsDuration.Subscribe()
0002rhs.OnNext(6)
0006rhsDuration.Subscribe()
0006rhsDuration.OnCompleted()
0006rhsDuration.Dispose()
OnNext
Apple 6
OnNext
Banana 6
0003lhsDuration.OnNext(Banana)
0003lhsDuration.Dispose()
...
This gives us some insight, however when the issue really becomes clear is when we start annotating our logs with a logical list of subscriptions.
In the original (working) code with the RefCount our annotations might look like this
//word.Subsribers.Add(wordPub)
0001lhs.Subscribe() //wordPub.Subsribers.Add(0001lhs)
0002rhs.Subscribe() //word.Subsribers.Add(0002rhs)
0001lhs.OnNext(Apple)
0003lhsDuration.Subscribe() //wordPub.Subsribers.Add(0003lhsDuration)
0002rhs.OnNext(5)
0004rhsDuration.Subscribe()
0004rhsDuration.OnCompleted()
0004rhsDuration.Dispose()
OnNext
Apple 5
0001lhs.OnNext(Banana)
0005lhsDuration.Subscribe() //wordPub.Subsribers.Add(0005lhsDuration)
0003lhsDuration.OnNext(Banana)
0003lhsDuration.Dispose() //wordPub.Subsribers.Remove(0003lhsDuration)
0002rhs.OnNext(6)
0006rhsDuration.Subscribe()
0006rhsDuration.OnCompleted()
0006rhsDuration.Dispose()
OnNext
Banana 6
So in this example, when word.OnNext("Banana"); is executed the chain of observers is linked in this order
wordPub
0002rhs
However, wordPub has child subscriptions!
So the real subscription list looks like
wordPub
0001lhs
0003lhsDuration
0005lhsDuration
0002rhs
If we annotate the Subject only log we see where the subtlety lies
0001lhs.Subscribe() //word.Subsribers.Add(0001lhs)
0002rhs.Subscribe() //word.Subsribers.Add(0002rhs)
0001lhs.OnNext(Apple)
0003lhsDuration.Subscribe() //word.Subsribers.Add(0003lhsDuration)
0002rhs.OnNext(5)
0004rhsDuration.Subscribe()
0004rhsDuration.OnCompleted()
0004rhsDuration.Dispose()
OnNext
Apple 5
0001lhs.OnNext(Banana)
0005lhsDuration.Subscribe() //word.Subsribers.Add(0005lhsDuration)
0002rhs.OnNext(6)
0006rhsDuration.Subscribe()
0006rhsDuration.OnCompleted()
0006rhsDuration.Dispose()
OnNext
Apple 6
OnNext
Banana 6
0003lhsDuration.OnNext(Banana)
0003lhsDuration.Dispose()
So in this example, when word.OnNext("Banana"); is executed the chain of observers is linked in this order
1. 0001lhs
2. 0002rhs
3. 0003lhsDuration
4. 0005lhsDuration
As the 0003lhsDuration subscription is activated after the 0002rhs, it wont see the "Banana" value to terminate the window, until after the rhs has been sent the value, thus yielding it in the still open window.
Whew
As #francezu13k50 points out the obvious and simple solution to your problem is to just use word.Select(x => new { Word = x, Length = x.Length });, but as I think you have given us a simplified version of your real problem (appreciated) I understand why this isn't suitable.
However, as I dont know what your real problem space is I am not sure what to suggest to you to provide a solution, except that you have one with your current code, and now you should know why it works the way it does.
RefCount returns an Observable that stays connected to the source as long as there is at least one subscription to the returned Observable. When the last subscription is disposed, RefCount disposes it's connection to the source, and reconnects when a new subscription is being made. It might be the case with your report query that all subscriptions to the 'wordPub' are disposed before the query is fulfilled.
Instead of the complicated GroupJoin query you could simply do :
var report = word.Select(x => new { Word = x, Length = x.Length });
Edit:
Change your report query to this if you want to use the GroupJoin operator :
var report =
wordPub
.GroupJoin(length,
s => wordPub,
s => Observable.Empty<int>(),
(w, a) => new { Word = w, Lengths = a })
.SelectMany(i => i.Lengths.FirstAsync().Select(j => new { Word = i.Word, Length = j }));
Because GroupJoin seems to be very tricky to work with, here is another approach for correlating the inputs and outputs of functions.
static void Main(string[] args) {
var word = new Subject<string>();
var length = new Subject<int>();
var report =
word
.CombineLatest(length, (w, l) => new { Word = w, Length = l })
.Scan((a, b) => new { Word = b.Word, Length = a.Word == b.Word ? b.Length : -1 })
.Where(i => i.Length != -1);
report.Subscribe(i => Console.WriteLine($"{i.Word} {i.Length}"));
word.OnNext("Apple"); length.OnNext(5);
word.OnNext("Banana");
word.OnNext("Cat"); length.OnNext(3);
word.OnNext("Donkey");
word.OnNext("Elephant"); length.OnNext(8);
word.OnNext("Zebra"); length.OnNext(5);
Console.ReadLine();
}
This approach works if every input has 0 or more outputs subject to the constraints that (1) outputs only arrive in the same order as the inputs AND (2) each output corresponds to its most recent input. This is like a LeftJoin - each item in the first list (word) is paired with items in the right list (length) that subsequently arrive, up until another item in the first list is emitted.
Trying to use regular Join instead of GroupJoin. I thought the problem was that when a new word was created there was a race condition inside Join between creating a new window and ending the current one. So here I tried to elimate that by pairing every word with a null signifying the end of the window. Doesn't work, just like the first version did not. How is it possible that a new window is created for each word without the previous one being closed first? Completely confused.
static void Main(string[] args) {
var lgr = new DelegateLogger(Console.WriteLine);
var word = new Subject<string>();
var wordDelimited =
word
.Select(i => Observable.Return<string>(null).StartWith(i))
.SelectMany(i => i);
var wordStart = wordDelimited.Where(i => i != null);
var wordEnd = wordDelimited.Where(i => i == null);
var report = Observable
.Join(
wordStart.Log(lgr, "word"), // starts window
wordStart.Select(i => i.Length),
s => wordEnd.Log(lgr, "expireWord"), // ends current window
s => Observable.Empty<int>(),
(l, r) => new { Word = l, Length = r });
report.Subscribe(i => Console.WriteLine($"{i.Word} {i.Length}"));
word.OnNext("Apple");
word.OnNext("Banana");
word.OnNext("Cat");
word.OnNext("Zebra");
word.OnNext("Elephant");
word.OnNext("Bear");
Console.ReadLine();
}
I have the following two observables
System.Net.WebRequest req = System.Net.HttpWebRequest.Create("http://test.com/data.xml");
req.Method = "HEAD";
var ob = Observable.FromAsyncPattern(req.BeginGetResponse, req.EndGetResponse);
ob().Select(x => x).Select(x => x.Headers["Last-Modified"]).DistinctUntilChanged(x => x);
Observable
.Interval(TimeSpan.FromSeconds(1.0))
.Select(_ => XDocument.Load("http://test.com/data.xml"));
I would like it that the XDocument observable is only executed when "last-modified" header is greater then the previously requested document any ideas?
Firstly .Select(x=>x) is a no-op so you can remove that.
I would change the code up a little bit. First lets break it down into its constituent parts:
1) The Timer. Every second poll the server.
var poll = Observable.Interval(TimeSpan.FromSeconds(1));
2) The call to get the header
var lastModified = Observable.FromAsyncPattern(req.BeginGetResponse, req.EndGetResponse).Select(x => x.Headers["Last-Modified"]);
3) The Select to get the Document
.Select(_ => XDocument.Load("http://test.com/data.xml"));
We should be able to compose that nicely:
var lastModified = from interval in Observable.Interval(TimeSpan.FromSeconds(1))
from response in Observable.FromAsyncPattern(req.BeginGetResponse, req.EndGetResponse)
select response.Headers["Last-Modified"];
var data = lastModified.DistinctUntilChanged().Select(_ => XDocument.Load("http://test.com/data.xml"));
data.Subscribe(dataXml=>
{
Console.WriteLine("Data has changed!");
Console.WriteLine(datXml);
});
Cavet I just typed that straight into the browser. I would be amazing if it compiles.