I have a string like string strn = "abcdefghjiklmnopqrstuvwxyz" and want a dictionary like:
Dictionary<char,int>(){
{'a',0},
{'b',1},
{'c',2},
...
}
I've been trying things like
strn.ToDictionary((x,i) => x,(x,i)=>i);
...but I've been getting all sorts of errors about the delegate not taking two arguments, and unspecified arguments, and the like.
What am I doing wrong?
I would prefer hints over the answer so I have a mental trace of what I need to do for next time, but as per the nature of Stackoverflow, an answer is fine as well.
Use the .Select operator first:
strn
.Select((x, i) => new { Item = x, Index = i })
.ToDictionary(x => x.Item, x => x.Index);
What am I doing wrong?
You're assuming there is such an overload. Look at Enumerable.ToDictionary - there's no overload which provides the index. You can fake it though via a call to Select:
var dictionary = text.Select((value, index) => new { value, index })
.ToDictionary(pair => pair.value,
pair => pair.index);
You could try something like this:
string strn = "abcdefghjiklmnopqrstuvwxyz";
Dictionary<char,int> lookup = strn.ToCharArray()
.Select( ( c, i ) => new KeyValuePair<char,int>( c, i ) )
.ToDictionary( e => e.Key, e => e.Value );
Related
Okay, I have a weird issue and I'm not sure if it's just because they're funcs and funcs are quite new to me (the whole idea of a func annoys me a bit).
I need to call the exact same query twice, because I need 2 graphs/data sets. One for each parameter. Because I don't want to duplicate my query, I've added a func parameter with my class, so I can strongly typed select the correct property on my class.
SearchDescriptor<Stats> Query(SearchDescriptor<Stats> qc, Func<Stats, double> field)
{
var query = qc
.Aggregations(aggs => aggs
.DateHistogram("histogram", d => d
.Field(f => f.Timestamp)
.FixedInterval("30m")
.MinimumDocumentCount(1)
.Aggregations(childAggs => childAggs
.Average("3", f => f
.Field(fi => field(fi))
)
)
)
)
.Query(q =>
q.Bool(b =>
b.Filter(
f => f.MatchPhrase(mp => mp
.Field(fi => fi.Name)
.Query(name)
),
f => f.DateRange(r => r
.Field(fi => fi.Timestamp)
.GreaterThanOrEquals(DateTime.UtcNow.AddDays(-1))
.LessThanOrEquals(DateTime.UtcNow)
)
)
)
);
return query;
}
var cpu = await GetByScrolling<Stats>(client, qc => Query(qc, fi => fi.CpuPercent));
var memory = await GetByScrolling<Stats>(client, qc => Query(qc, fi => fi.MemoryUsage));
This compiles and should work, but unfortunately the generated JSON turns out like this:
"aggs": {
"3": {
"avg": {
"field": "field"
}
}
}
As you can see, the value is field, which is definitely wrong. It's like the internal part of NEST does some kind of .ToString() on whatever argument is there, which is why it's returning field instead of what I put in my func.
Any idea if this is supposed to be like this? The aggs in my JSON also corresponds to the .Aggregations(aggs => aggs lambda naming.
Ah, as I said, I am new to funcs and also Expression, which turns out is what I need. It doesn't actually evaluate the func - I need to give it an expression.
SearchDescriptor<Stats> Query(SearchDescriptor<Stats> qc, Expression<Func<Stats, double>> field)
{
......
}
and then give it the expression:
.Average("3", f => f
.Field(field)
)
Then it evaluates it and works perfectly.
I have the following Dictionary:
public Dictionary<string,object> Items;
Now I need to get all Items where the Value of the Dictionary-item is from a specific type. (e.g. "int")
var intValues = Items.OfType<KeyValuePair<string,int>> simply does not work.
Code without LinQ would be something like:
var intValues=new Dictionary<string,int>()
foreach (var oldVal in Items) {
if (oldVal.Value is int) {
intValues.add(oldVal.Key, oldVal.Value);
}
}
(Update) my example should show the basic idea. But if possible I would avoid to create a new Dictionary as a result.
The direct translation of your foreach would be the following in LINQ:
var intValues = Items.Where(item => item.Value is int)
.ToDictionary(item => item.Key, item => (int)item.Value);
So basically, you filter first for where the item.Value is an int, and then you create a dictionary from it using ToDictionary where you cast those values to int to make sure that the resulting dictionary is a Dictionary<string, int>. Since we filtered non-integers already, this type cast will always succeed.
You can use the is operator on the Value property:
var intValues = Items.Where(x => x.Value is int);
If you want an actual Dictionary<string,int> at the end just add:
.ToDictionary(v=> v.Key, v=> (int)v.Value)
Try with this:
var intValue = Items
.Where(x => x.Value is int) // filter per Value is int
.ToDictionary(x => x.Key, x => (int)x.Value); // create a new dictionary converting Value to int
You can do
var result = Items.Where(x => x.Value is int)
.ToDictionary(x => x.Key, x => x.Value);
Dictionary<int, string> names= GetNames().Where(x =>
x.Value.StartsWith("test") | x.Value.StartsWith(" " + "test")).ToDictionary(x => x.Key, y => y.Value);
The values from the getNames() method are something like this:
John Testing
Test Travis
Test Metsaoy
Using the above line of code I'm getting only the two last entries, but I want also the 1st one because the 2nd string starts with "test".
So, I need to modify the above where statement. I tried something like this:
.Where(x =>
foreach ( xx in x.Value.Split(' ') ) { if ( xx.StartsWith("text") ) true; })
How can I achieve this?
var res = GetNames().Where(kvp => kvp.Value.Split()
.Any(s => s.StartsWith("Test") || s.StartsWith("test")));
Optionally instead of StartsWith you can use String.Contains in the Any lambda.
Try:
var parser = new Regex(#"\bTest", RegexOptions.Compiled);
GetNames().Where(x => parser.IsMatch(x.Value)).ToDictionary(x => x.Key, y => y.Value)
Have you tried this?
x.Value.StartsWith("test") || x.Value.Contains(" test")
You'll have to use it in your query like this:
var names= GetNames()
.Where(x => x.Value.StartsWith("test") || x.Value.Contains(" test"))
.ToDictionary(x => x.Key, y => y.Value);
Hope it helps
This is related to my other question here. James World presented a solution as follows:
// idStream is an IObservable<int> of the input stream of IDs
// alarmInterval is a Func<int, TimeSpan> that gets the interval given the ID
var idAlarmStream = idStream
.GroupByUntil(key => key, grp => grp.Throttle(alarmInterval(grp.Key)))
.SelectMany(grp => grp.IgnoreElements().Concat(Observable.Return(grp.Key)));
<edit 2:
Question: How do I start the timers immediately without waiting for the first events to arrive? That's the root problem in my question, I guess. For that end, I planned on sending off dummy objects with the IDs I know should be there. But as I write in following, I ended up with some other problems. Nevertheless, I'd think solving that too would be interesting.
Forwards with the other interesting parts then! Now, if I'd like to group a complex object like the following and group by the key as follows (won't compile)
var idAlarmStream = idStream
.Select(i => new { Id = i, IsTest = true })
.GroupByUntil(key => key.Id, grp => grp.Throttle(alarmInterval(grp.Key)))
.SelectMany(grp => grp.IgnoreElements().Concat(Observable.Return(grp.Key)));
then I get into trouble. I'm unable to modify the part about SelectMany, Concat and Observable.Return so that the query would work as before. For instance, if I make query as
var idAlarmStream = idStream
.Select(i => new { Id = i, IsTest = true })
.GroupByUntil(key => key.Id, grp => grp.Throttle(alarmInterval(grp.Key)))
.SelectMany(grp => grp.IgnoreElements().Concat(Observable.Return(grp.Key.First())))
.Subscribe(i => Console.WriteLine(i.Id + "-" + i.IsTest);
Then two events are needed before an output can be observed in the Subscribe. It's the effect of the call to First, I gather. Furthermore, I woul like to use the complex object attributes in the call to alarmInterval too.
Can someone offer an explanation what's going on, perhaps even a solution? The problem in going with unmodified solution is that the grouping doesn't look Ids alone for the key value, but also the IsTest field.
<edit: As a note, the problem probably could be solved firsly by creating an explicit class or struct and then that implements a custom IEquatable and secondly then using James' code as-is so that grouping would happen by IDs alone. It feels like hack though.
Also, if you want to count the number of times you've seen an item before the alarm goes off you can do it like this, taking advantage of the counter overload in Select.
var idAlarmStream = idStream
.Select(i => new { Id = i, IsTest = true })
.GroupByUntil(key => key.Id, grp => grp.Throttle(alarmInterval(grp.Key))
.SelectMany(grp => grp.Select((count, alarm) => new { count, alarm }).TakeLast(1));
Note, this will be 0 for the first (seed) item - which is probably what you want anyway.
You are creating an anonymous type in your Select. Lets call it A1. I will assume your idStream is an IObservable. Since this is the Key in the GroupByUntil you do not need to worry about key comparison - int equality is fine.
The GroupByUntil is an IObservable<IGroupedObservable<int, A1>>.
The SelectMany as written is trying to be an IObservable<A1>. You need to just Concat(Observable.Return(grp.Key)) here - but the the type of the Key and the type of the Group elements must match or the SelectMany won't work. So the key would have to be an A1 too. Anonymous types use structural equality and the return type would be stream of A1 - but you can't declare that as a public return type.
If you just want the Id, you should add a .Select(x => x.Id) after the Throttle:
var idAlarmStream = idStream
.Select(i => new { Id = i, IsTest = true })
.GroupByUntil(key => key.Id, grp => grp.Throttle(alarmInterval(grp.Key)
.Select(x => x.Id))
.SelectMany(grp => grp.IgnoreElements().Concat(Observable.Return(grp.Key)));
If you want A1 instead - you'll need to create a concrete type that implements Equality.
EDIT
I've not tested it, but you could also flatten it more simply like this, I think this is easier! It is outputing A1 though, so you'll have to deal with that if you need to return the stream somewhere.
var idAlarmStream = idStream
.Select(i => new { Id = i, IsTest = true })
.GroupByUntil(key => key.Id, grp => grp.Throttle(alarmInterval(grp.Key))
.SelectMany(grp => grp.TakeLast(1));
I have a function which returns a list of property values from a collection:
public static List<string> GetSpeakerList()
{
var Videos = QueryVideos(HttpContext.Current);
return Videos.Where(v => v.Type == "exampleType"
.SelectMany(v => v.SpeakerName)
.Distinct()
.OrderBy(s => s)
.ToList();
}
I'd like to have a generic version which will let me determine which field I'd like projected - say instead of SpeakerName I'd like to allow selecting Video.Length or Video.Type.
I understand that SelectMany takes a Func, so what's the best way to make the Func configurable to allow passing it as a parameter into this function?
Add the function as a parameter to the method.
public static List<string> GetVideosAttribute( Func<Video,string> selector )
{
var Videos = QueryVideos(HttpContext.Current);
return Videos.Where(v => v.Type == "exampleType"
.Select( selector )
.Distinct()
.OrderBy(s => s)
.ToList();
}
var speakers = GetVideosAttribute( v => v->SpeakerName );
var topics = GetVideosAttribute( v => v->Topic );