I am using the ReactiveUI framework to do searching for a list of airports in the world.
I have setup the ObservableAsPropertyHelper that is the output of the suggested airports from a SearchTerm property in the ViewModel. The following is the definition for the ObservableAsPropertyHelper. In the view, I have a listbox that binds to this property. I would like to be able to clear the listbox explicitly(because once the user selected a suggested item, I want to populate the SearchTerm with the selected airport and clear the suggested list). Is there an elegant way to implement this?
var searchTerms = this.ObservableForProperty(x => x.SearchTerms).Where(x => canSearch).Value().Throttle(TimeSpan.FromMilliseconds(500));
var searchResults = searchTerms.SelectMany(SearchAirports);
var latestResults = searchTerms.CombineLatest(searchResults, (s, r) => r.SearchTerm != s ? null : r.AirportLiteWithWeights).Where(x => x != null);
_airportLiteWithWeights = latestResults.ToProperty(this, x => x.AirportLiteWithWeights);
Here's how I would do it - it's a bit tricky because the actual sequence of events feeds back into itself (i.e. selecting an item sets SearchTerms)
// The act of selecting an item is very ICommand'y, let's model it
// as such.
ReactiveCommand SuggestionItemSelected = new ReactiveCommand();
// Important to *not* signal a change here by touching the field
// so that we avoid a circular event chain
SuggestionItemSelected.Subscribe(x => _searchTerms = x);
// NB: Always provide a scheduler to Throttle!
var searchTerms = this.WhenAnyValue(x => x.SearchTerms)
.Where(x => canSearch)
.Throttle(TimeSpan.FromMilliseconds(500), RxApp.MainThreadScheduler);
// Select + Switch will do what you were trying to do with CombineLatest
var latestResults = searchTerms
.Select(SearchAirports);
.Switch()
// The listbox is the combination of *Either* the latest results, or the
// empty list if someone chooses an item
var latestResultsOrCleared = Observable.Merge(
latestResults,
SuggestionItemSelected.Select(_ => new List<Results>()));
latestResultsOrCleared
.ToProperty(this, x => x.AirportLiteWithWeights, out _airportLiteWithWeights);
Related
Inside method I have a list that contains grouped data:
var listofData = _context.DBONE.where(x => x.Id==3 && x.Status!=0)
.GroupBy(x => new { x.Name, x.Class })
.Select(q => new { Result = q.ToList() }).ToList();
if (methodParam == 10)
{
data = listofData.Where(x => FunctionCheck(---CANNOT ACCESS THE FIELDS FROM GROUP DATA TO PASS AS PARAMETERS---) == 10).ToList();
}
And this is the function that will receive 2 parameter from the grouped data:
private int FunctionCheck(int id, string name)
{...}
But, I cannot access the desired field inside 'listofData'. I can access only in case the listofData is not using groupBy().
If I understand what you are trying to do correctly, you are able to access your data but you are actually creating a "list of a list"
Watch my example, I think I have reproduced your scenario here:
As you can see, I then have a "result" which contains a list of users where Id == 3. The problem is that you create a new anonymous object with a props that is a list. So if you try the last thing you see in my image above, I think you will be able to access your rows.
The reason is that after your GroupBy call, the result is of a grouping type - every item of your list is an Enumerable of the original item, so you would have to operate on that grouping in a following manner:
// Groups such that all items in that group pass your check
listofData
.Where(group => group.All(item => FunctionCheck(item.Id, item.Name) == 10))
.ToList();
// Groups where at least one item matches
listofData
.Where(group => group.Any(item => FunctionCheck(item.Id, item.Name) == 10))
.ToList();
The desired outcome is not really clear from the question but this is the step you are likely missing.
Another approach which might be potentially useful is pre-filter the colleciton of items before grouping them:
var listOfGroupedDatas = _context.DBONE
.Where(x => x.Id ==3 && x.Status != 0 && FunctionCheck(item.Id, item.Name) == 10)
.GroupBy(x => new { x.Name, x.Class })
.ToList();
// This will result in a list of groupings in which all items pass your check
I think you want to call SelectMany to project into one dimensional array.
var listofData = _context.DBONE.where(x => x.Id==3 && x.Status!=0)
.GroupBy(x => new { x.Name, x.Class })
.SelectMany(q => q.ToList()).ToList();
Check the code bellow. Here i am creating a method that simply should remove the duplicate from the list foo. If you see the list values they are product id and quantity derived by : so the first part of number before : is product and and second part of number after : is the product quantity. I am taking this list into RemoveDuplicateItems() method for processing. This method should remove all matching product id items from whole list but my current method just returns exactly same list which i am taking on input. How can i fix my method to remove those item from list which has matching first part number. (first part number means before :)
The final output on doo variable it should remove the first from from list which is 22:15 since it has matching with second one.
C#:
[HttpPost]
public JsonResult DoSomething()
{
var foo = new List<string>();
foo.Add("22:10");//this should removed by RemoveDuplicateItems() since it has `22` matching with second one
foo.Add("22:15");
foo.Add("25:30");
foo.Add("26:30");
var doo = RemoveDuplicateItems(foo);
return Json("done");
}
public List<string> RemoveDuplicateItems(List<string> AllItems)
{
var FinalList = new List<string>();
var onlyProductIds = new List<string>();
foreach (var item in AllItems)
{
Match result = Regex.Match(item, #"^.*?(?=:)");
onlyProductIds.Add(result.Value);
}
var unique_onlyProductIds = onlyProductIds.Distinct().ToList();
foreach (var item in AllItems)
{
Match result = Regex.Match(item, #"^.*?(?=:)");
var id = unique_onlyProductIds.Where(x => x.Contains(result.Value)).FirstOrDefault();
if (id != null)
{
FinalList.Add(item);
}
}
return FinalList;
}
Does this work for you?
List<string> doo =
foo
.Select(x => x.Split(':'))
.GroupBy(x => x[0], x => x[1])
.Select(x => $"{x.Key}:{x.Last()}")
.ToList();
There are multiple ways to achieve this, one is, as suggested by #Aluan Haddad is to use Linq. His comment uses the query syntax but would could use the method syntax too (I assumed you use C#8):
List<string> doo = foo.GroupBy(str => str[0..2])
.Select(entry => entry.Last())
.ToList();
Note that this works because the current implementation of GroupBy preserves ordering.
you can do it using Linq :
var doo = foo.Select(x =>
{
var split = x.Split(':');
return new { Key = split[0], Value = split[1] };
})
.GroupBy(x => x.Key)
.OrderBy(x => x.Key)
.Select(x =>
{
var max = x.LastOrDefault();
return $"{max.Key}:{max.Value}";
}
).ToList();
The objects are WPF specific, but same thing...
var v = Style.Triggers.Where(x => x is EventTrigger)
.Cast<EventTrigger>()
.Select(x => x.Actions);
At this point, I get 3 TriggerActionCollections which is correct. What I want to do next is select the items within each collection that are "is BeginStoryboard". I can't seem to work out how to select the items within Actions (the TriggerActionsCollection).
I was thinking something like this:
var v = Style.Triggers.Where(x => x is EventTrigger)
.Cast<EventTrigger>()
.Select(x => x.Actions.Select(y => y).Where(y => y is BeginStoryboard));
But that isn't working. Any help guys?
For those non-wpf folks. Yes, there are 3 TriggerActionCollections and in one of those there is a BeginStoryBoard object. But for arguments sake I want EVERY BeginStoryBoard object flattened out.
Have you tried
var v = Style.Triggers.Where(x => x is EventTrigger)
.Cast<EventTrigger>()
.SelectMany(x => x.Actions)
.Where(...)
SelectMany instead of Select returns single collection instead of collection of collections.
var v = Style.Triggers
.OfType<EventTrigger>()
.SelectMany(x => x.Actions)
.OfType<BeginStoryboard>();
I'm quite new to elasticsearch, I am using NEST to query to elastic following is my code snippet.
var searchResults =
elasticClient.Client.Search<T>(
s => s
.Size(20)
.Fields(core)
.QueryString(String.Format("*{0}*", query)).MinScore(1).QueryString(String.Format("*{0}*", query.Replace(" ", "")))
.Highlight(h => h
.PreTags("<b>")
.PostTags("</b>")
.OnFields(f => f
.PreTags("<em>")
.PostTags("</em>")
)
)
);
var suggestResults = elasticClient.Client.Suggest<T>(s => s
.Term("suggest", m => m
.SuggestMode(SuggestMode.Always)
.Text(query)
.Size(10)
.OnField(core)
));
var aggregation = elasticClient.Client.Search<T>(s => s
.Aggregations(a => a
.Terms("term_items", gh=>gh
.Field(p=>p.Town)
.Aggregations(gha=>gha
.SignificantTerms("bucket_agg", m => m
.Field(p => p.Town)
.Size(2)
.Aggregations(ma => ma.Terms("Town", t => t.Field(p => p.Town)))
)
)
)
)
);
I do get list of documents (list of my specified domain object) , but in case of suggest and aggregation it doesn't return domain object ?
I apologize in advanced and I hope you can point me to the correct direction.
I am looking for a way to implement in NEST.
To get to your aggregations you need to use the Aggs property of the result. According to the documentation:
The result of the aggregations are accessed from the Aggs property of the response using the key that was specified on the request...
In your example this would be "term_items". You are also doing a sub-aggregation, so these need to be extracted for each top-level aggregation, again using the key specified for the sub-aggregation - "bucket_agg". Your code should look something like
var agg = results.Aggs.Terms("term_items");
if (agg!= null && agg.Items.Count > 0)
{
foreach (var termItemAgg in agg.Items)
{
// do something with the aggregations
// the term is accessed using the Key property
var term = termItemAgg.Key;
// the count is accessed through the DocCount property
var count = termItemAgg.Count;
// now access the result of the sub-aggregation
var bucketAgg = termItemAgg.Aggs.SignificantTerms("bucket_agg");
// do something with your sub-aggregation results
}
}
There is more detail in the documentation.
To get your suggestions you access the Suggestions property of your results object, using the key you specify when calling the ElasticClient.Suggest method. Something like
var suggestions = result.Suggestions["suggest"]
.FirstOrDefault()
.Options
.Select(suggest => suggest.Payload)
.ToList();
Hope this helps .
I'm trying to combine two observables whose values share some key.
I want to produce a new value whenever the first observable produces a new value, combined with the latest value from a second observable which selection depends on the latest value from the first observable.
pseudo code example:
var obs1 = Observable.Interval(TimeSpan.FromSeconds(1)).Select(x => Tuple.create(SomeKeyThatVaries, x)
var obs2 = Observable.Interval(TimeSpan.FromMilliSeconds(1)).Select(x => Tuple.create(SomeKeyThatVaries, x)
from x in obs1
let latestFromObs2WhereKeyMatches = …
select Tuple.create(x, latestFromObs2WhereKeyMatches)
Any suggestions?
Clearly this could be implemented by subcribing to the second observable and creating a dictionary with the latest values indexable by the key. But I'm looking for a different approach..
Usage scenario: one minute price bars computed from a stream of stock quotes. In this case the key is the ticker and the dictionary contains latest ask and bid prices for concrete tickers, which are then used in the computation.
(By the way, thank you Dave and James this has been a very fruitful discussion)
(sorry about the formatting, hard to get right on an iPad..)
...why are you looking for a different approach? Sounds like you are on the right lines to me. It's short, simple code... roughly speaking it will be something like:
var cache = new ConcurrentDictionary<long, long>();
obs2.Subscribe(x => cache[x.Item1] = x.Item2);
var results = obs1.Select(x => new {
obs1 = x.Item2,
cache.ContainsKey(x.Item1) ? cache[x.Item1] : 0
});
At the end of the day, C# is an OO language and the heavy lifting of the thread-safe mutable collections is already all done for you.
There may be fancy Rx approach (feels like joins might be involved)... but how maintainable will it be? And how will it perform?
$0.02
I'd like to know the purpose of a such a query. Would you mind describing the usage scenario a bit?
Nevertheless, it seems like the following query may solve your problem. The initial projections aren't necessary if you already have some way of identifying the origin of each value, but I've included them for the sake of generalization, to be consistent with your extremely abstract mode of questioning. ;-)
Note: I'm assuming that someKeyThatVaries is not shared data as you've shown it, which is why I've also included the term anotherKeyThatVaries; otherwise, the entire query really makes no sense to me.
var obs1 = Observable.Interval(TimeSpan.FromSeconds(1))
.Select(x => Tuple.Create(someKeyThatVaries, x));
var obs2 = Observable.Interval(TimeSpan.FromSeconds(.25))
.Select(x => Tuple.Create(anotherKeyThatVaries, x));
var results = obs1.Select(t => new { Key = t.Item1, Value = t.Item2, Kind = 1 })
.Merge(
obs2.Select(t => new { Key = t.Item1, Value = t.Item2, Kind = 2 }))
.GroupBy(t => t.Key, t => new { t.Value, t.Kind })
.SelectMany(g =>
g.Scan(
new { X = -1L, Y = -1L, Yield = false },
(acc, cur) => cur.Kind == 1
? new { X = cur.Value, Y = acc.Y, Yield = true }
: new { X = acc.X, Y = cur.Value, Yield = false })
.Where(s => s.Yield)
.Select(s => Tuple.Create(s.X, s.Y)));