I would like to know whether there is any way to shorten a comparison in lambdas by using arrays containing the comparison elements, instead of writing the elements one by one.
In practice, I have a Dictionary<K,V> variable named litemList already filled with data. I would like to have another Dictionary<K,V> variable with just some of the Keys of litemList.
lfilteredItemlist = litemList.Where(m => m.Key == "Name", m.Key == "Surname")
.ToDictionary(m => m.Key, m => m.Value);
This code works perfectly but when I have 10 or more Keys to filter and they might change with time (or even selected by the users), this solution is not feasible.
I am looking for some solution where, assuming there is an array withall the Keys to filter, I can use something like this:
filterArray = {"Name", "Surname"};
lfilteredItemlist = litemList.Where(m => m.Key == filterArray)
.ToDictionary(m => m.Key, m => m.Value);
I am quite sure that there is a method because once I saw it when looking for material about Dynamic LINQ. Unfortunately I cannot find the article again and Scott Guthrie does not mention it in his blog.
Thanks
Francesco
How about changing the code to this:
lfilteredItemlist = litemList.Where(m => filterArray.Contains(m.Key))
.ToDictionary(m => m.Key, m => m.Value);
Related
I have a list of strings like this:
a#domain.com
b#sub.domain.com
c#sub.sub.domain.com
d#sub.domain2.com
I want to remove the subdomains and only leave the domain.com, domain2.com, etc..
What I have tried so far but with no success:
string[] campusCup(string[] emails)
{
var emailList = emails.Select(x => x.Split('#').Last())
.Distinct()
.Select(x => x.Where(y => x.Split('.').Length > 2).Select(z => x.Split('.').Reverse().Take(2).Reverse()))
.Select(x => x)
.Distinct();
return emailList.ToArray();
}
Any help solving the task or explanation of what I am doing wrong and how can I solve it is appreciated. Thank you
You could first use MailAddress to get the host, then some string methods to get only the last two:
string[] domains = emails
.Select(e => new MailAddress(e).Host.Split('.'))
.Select(arr => String.Join(".", arr.Skip(arr.Length - 2)))
.Distinct()
.ToArray();
This seems to work for me given your data set:
var domains = emails.Select(e => e.Split('#')[1]).Select(d =>
{
var parts = d.Split('.');
return string.Join(".", parts.Skip(parts.Length - 2));
}).Distinct();
If you just want to learn about LINQ, as you mention in the comments of your question, here is another fun option:
var reg = new Regex(#"[a-z0-9\.]+#[a-z0-9\.]*?(?<domain>[a-z0-9]+\.[a-z0-9]+)$");
var secondLevelDomains = domains.SelectMany(domainName => reg.Matches(domainName).Cast<Match>()
.Select(m => m.Groups["domain"])
.Select(m => m.Value))
.Distinct();
It uses matching groups in regular expressions to parse the domain names, and several of the more interesting LINQ functions, like Cast (for converting older collections in to LINQ friendly enumerables), SelectMany (to merge enumerable properties of multiple items), and Distinct (to return only unique entries).
This is probably not the ideal way to do this in a real application, but it exposes a lot of LINQ functionality for learning purposes.
I have this:
var myResult = uow.GetRepository<SLItemsCustomersCard, long>()
.Query(x => x.CustomerId == customerId && x.SquareColor == squareColor)
.OrderBy(x => x.BranchMapRang)
.Select((r, i) => new { Row = r, Index = i })
.Where(x => x.Index == visitCounter - 1).ToList();
but I want to achive this in where clause:
.Where(x => x.Index.Cotains(visitCounter)).ToList();
How to do this?
You seem to be misunderstanding what the Contains method does. I'm basing this answer on your earlier usage of:
Where(x => x.Index == visitCounter - 1)
In other words, visitCounter is an integer (and so is Index). But then you want to use it like this:
Where(x => x.Index.Contains(visitCounter))
Which does not make syntactical sense. An integer (Index) does not have a Contains function. It's not fully clear to me what you are trying to achieve, but your comment clarifies it a bit more:
But I want to achieve something like IN clause in SQL server.
The IN clause in SQL requires a range of possibilities (a list of integers, in your case), and you're not working with a list of integers here. Furthermore, you have phrased it as Index.Contains(visitCounter) which would imply that you're expecting Index to be the list of integers?
That simply doesn't make sense. So I'll give you the answer that makes the most sense, on the assumption that you weren't accurate with your pseudocode:
List<int> visitorIds = new List<int>() { 1, 5, 99, 125 };
And then you can do the following:
.Where(x => visitorIds.Contains(x.Index))
To put it in words, this snippet basically tells the computer to "only give the items whose Index is mentioned in the visitorIds list".
You can use Contains like this:
int[] VisitorIds = new int[] {1,2,3}; //an array to check with
.Where(x => vivitorIds.Contains(x.Index)).ToList();
I have a file with a simple key,value format, one per line.
e.g:
word1,filepath1
word2,filepath2
word3,filepath5
I'm trying to read this into a Dictionary<string,string> in one go with LINQ. There are some duplicates in the file (where the first part - the first string - is the duplicate). In this case, I'm ok with dropping the duplicates.
This is my LINQ which isn't working:
var indexes = File.ReadAllLines(indexFileName)
.Select(x => x.Split(','))
.GroupBy(x=>x[0])
.ToDictionary(x => x.Key, x => x.ElementAt(1));
The ToDictionary part is confusing me, how do I retrieve the first value from the group and assign it to the value of the dictionary?
I get a System.ArgumentOutOfRangeException: 'Specified argument was out of the range of valid values.' exception.
var indexes = File.ReadAllLines(indexFileName)
.Select(x => x.Split(','))
.GroupBy(x => x[0])
.ToDictionary(x => x.Key, x => x.First()[1]);
So the problem here is that you're grouping arrays, not strings. Therefore the group objects you're dealing with in the ToDictionary() lambda are enumerations of arrays, not of strings. g.ElementAt(0) isn't a string. It's the first array of strings:
When
g.Key == "word1"
then g.ElementAt(0) is...
{ "word1", "filepath1" }
So you want g.ElementAt(0).ElementAt(1), or g.First()[0], or something to that effect.
That seems painfully obvious in hindsight, but unfortunately only in hindsight, for me.
I would suggest that after you accept Matthew Whited's answer, you clarify the code by turning the split lines into anonymous objects as soon as you can. ElementAt(1) doesn't communicate much.
var indexes =
File.ReadAllLines(indexFileName)
.Where(s => !String.IsNullOrEmpty(s))
.Select(x => x.Split(','))
// Turn the array into something self-documenting
.Select(a => new { Word = a[0], Path = a[1] })
.GroupBy(o => o.Word)
.ToDictionary(g => g.Key, g => g.First().Path)
;
Converting each line to an object makes it easier for me to think about, and Intellisense starts playing on your team as well.
I have a resource file I grab like this:
var rs = <somewhere>.FAQ.ResourceManager
.GetResourceSet(CultureInfo.CurrentUICulture, true, true);
and I want to parse it into a dictionary of dictionaries but I can't figure out quite how. this is what I'm trying:
var ret = rs.OfType<DictionaryEntry>()
.Where(x => x.Key.ToString().StartsWith("Title"))
.ToDictionary<string, Dictionary<String, string>>(
k => k.Value.ToString(),
v => rs.OfType<DictionaryEntry>()
.Where(x => x.Key.ToString().StartsWith(v.Value.ToString().Replace("Title", "")))
.ToDictionary<string, string>(
key => key.Value,
val => val.Value
)
);
so if I understand this correctly, k should refer to a DictionaryEntry and thus I should be able to dereference it like k.Value and to manufacture my dictionary in each of the outer dictionary's entries I do another query against the resource file, thus key and val should also be of type DictionaryEntry.
In referencing val.Value I get the error "Cannot choose method from method group. Did you intend to invoke the method?" though that should be a property, not a method.
help?
p.s. as an explanation, my resource file looks sort of like this:
TitleUser: User Questions
TitleCust: Customer Questions
User1: Why does something happen? Because…
User2: How do I do this? Start by…
Cust1: Where can I find…? It is located at…
Cust2: Is there any…? yes, look for it…
which means I first get a list of sections (by looking for all keys that start with "Title") and for each I look for a list of questions
so the answer turns out to be that the compiler knows better as to the types involved. leaving out the specifiers makes it work, though quite why my specifiers were wrong I don't yet get.
var ret = rs.OfType<DictionaryEntry>()
.Where(x => x.Key.ToString().StartsWith("Title"))
.ToDictionary(
k => k.Value.ToString(),
v => rs.OfType<DictionaryEntry>()
.Where(x => x.Key.ToString().StartsWith(v.Key.ToString().Replace("Title", "")))
.ToDictionary(
x => x.Value.ToString().Split('?')[0] + "?",
x => x.Value.ToString().Split('?')[1]
)
);
(I've made some changes to actually make it do what I intended for it to do).
I have a list of objects that I need some duplicates removed from. We consider them duplicates if they have the same Id and prefer the one whose booleanValue is false. Here's what I have so far:
objects.GroupBy(x => x.Id).Select(x => x.Where(y => !y.booleanValue));
I've determined that GroupBy is doing no such grouping, so I don't see if any of the other functions are working. Any ideas on this? Thanks in advance.
You can do this:
var results =
from x in objects
group x by x.Id into g
select g.OrderBy(y => y.booleanValue).First();
For every Id it finds in objects, it will select the first element where booleanValue == false, or the the first one (if none of them have booleanValue == false).
If you prefer fluent syntax:
var results = objects.GroupBy(x => x.Id)
.Select(g => g.OrderBy(y => y.booleanValue).First());
Something like this should work:
var result =
objects.GroupBy(x => x.Id).Select(g =>
g.FirstOrDefault(y => !y.booleanValue) ?? g.First())
This assumes that your objects are of a reference type.
Another possibility might be to use Distinct() with a custom IEqualityComparer<>.
This partially answers the question above, but I justed need a really basic solution:
objects.GroupBy(x => x.Id)
.Select(x => x.First())
.ToArray();
The key to getting the original object from the GroupBy() is the Select() getting the First() and the ToArray() gets you an array of your objects, not a Linq object.