Order By on the Basis of Integer present in string - c#

I've a problem in my C# application... I've some school classes in database for example 8-B, 9-A, 10-C, 11-C and so on .... when I use order by clause to sort them, the string comparison gives results as
10-C
11-C
8-B
9-A
but I want integer sorting on the basis of first integer present in string...
i.e.
8-B
9-A
10-C
11-C
hope you'll understand...
I've tried this but it throws exception
var query = cx.Classes.Select(x=>x.Name)
.OrderBy( x=> new string(x.TakeWhile(char.IsDigit).ToArray()));
Please help me... want ordering on the basis of classes ....

Maybe Split will do?
.OrderBy(x => Convert.ToInt32(x.Split('-')[0]))
.ThenBy(x => x.Split('-')[1])

If the input is well-formed enough, this would do:
var maxLen = cx.Classes.Max(x => x.Name.Length);
var query = cx.Classes.Select(x => x.Name).OrderBy(x => x.PadLeft(maxLen));

You can add 0 as left padding for a specified length as your data for example 6
.OrderBy(x => x.PadLeft(6, '0'))

This is fundamentally the same approach as Andrius's answer, written out more explicitly:
var names = new[] { "10-C", "8-B", "9-A", "11-C" };
var sortedNames =
(from name in names
let parts = name.Split('-')
select new {
fullName = name,
number = Convert.ToInt32(parts[0]),
letter = parts[1]
})
.OrderBy(x => x.number)
.ThenBy(x => x.letter)
.Select(x => x.fullName);
It's my naive assumption that this would be more efficient because the Split is only processed once in the initial select rather than in both OrderBy and ThenBy, but for all I know the extra "layers" of LINQ may outweigh any gains from that.

Related

Remove sub-domains from list of domains using LINQ

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.

how to use Contains in where clause in linq

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();

Why isn't this LINQ parsing a file properly?

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.

How to order group results in linq

I have a list of files say prg_3.txt , prg_2.txt , prg_1.txt .
I need to loop over the files and merge the files in order 1,2,3 .
The query i am using is as follows:
var Groups = shortfilenames.GroupBy(s => s.Substring(0, s.IndexOf('_'))).ToList();
The above query would create a group names prg and it will have 3 files.
Now,i need to sort them in the order 1,2,3 i.e fromm their file names.
Here, I am getting grouped results, but i am not sure how to order the elements in each group
Please help..let me know incase of any questions..
Edited :
Will it be good enough ?
var userGroups = shortfilenames.GroupBy(s => s.Substring(0, s.IndexOf('_'))).Select(g=>g.OrderBy(x=>x.Substring(x.IndexOf('_',x.Length-x.IndexOf('_')))));
This should work but probably won't be so efficient:
shortfilenames
.GroupBy(s => s.Substring(0, s.IndexOf('_')))
.Select(
g => g.OrderBy(x => int.Parse(new String(x.Where(char.IsDigit).ToArray()))));
This will not work if your file contains additional digits, here is another solution to fix that, according to your comment this should work with the format you specified:
shortfilenames
.GroupBy(s => s.Substring(0, s.IndexOf('_')))
.Select(g => g.OrderBy(
x =>
{
var index = x.IndexOf('_');
return int.Parse(x.Substring(index + 1, x.LastIndexOf('.') - index));
}));
Since the names kinda match, what's the problem with simply using the OrderBy and giving it the names ?
var v = new string[] {"prg_3.txt","prg_2.txt", "prg_1.txt"};
var sorted = v.OrderBy(name => name);
you get :
prg_1.txt
prg_2.txt
prg_3.txt
If you want to sort inner groupings by file name this should do the trick:
shortfilenames.GroupBy(s => s.Substring(0, s.IndexOf('_'))).Select(g => g.OrderBy(e => e)).ToList();

Chained selects with where at the end

Given following structure: a person has functions. Each function has roles. Each roles has features. Now I would like to figure out with linq if a given person has a certain feature, but I am doing something wrong with this query. As a result I always get the count of the functions (but I'd like to get the count of the features):
var count = person.Functions
.Select(fu => fu.Roles
.Select(r => r.Features
.Where(f => f.FeatureId == 99999)))
.Count();
What am I doing wrong here? According to this query I expect either 0 (hasn't got the feature) or 1.
var query = from function in person.Functions
from role in function.Roles
from feature in role.Features
where feature.FeatureId == 99999
select feature;
var count = query.Count();
or
var count = person.Functions
.SelectMany(function => function.Roles)
.SelectMany(role => role.Features)
.Count(feature => feature.FeatureId == 99999);
If you don't need the exact count but just want to know if the person has the feature or not, use Any instead of Count.
var count = person.Functions
.SelectMany(p => p.Roles)
.SelectMany(r => r.Features)
.Where(f => f.FeatureId == 99999)
.Count();
I'm not really sure, but I think you want the total number of Features with teh given Id. You would want to use SelectMany.

Categories

Resources