Sample:
var aux = new int[] { -1,0,1,-1,2,3,4,5,6,7 }
Expected result:
{ 2,3,4,5,6,7,1,0,-1,-1 }
How?
--
Edit: Sorry for the poor question.
I want to order all from the value 2, and others who are put in the lower end (I fixed the text).
var query = aux
.OrderBy(i => i < 2 ? 2 : 1) //small numbers last
.ThenBy(i => i < 2 ? -i : i); //large numbers asc, small numbers desc
var res = aux.Where(a => a>=2 ).OrderBy (a => a)
.Concat(aux.Where (a => a <2).OrderByDescending (a => a))
Related
I am trying to convert FOO BAR BAZ to "GPCSC[", "N##" using linq.
I came this close:
var res2 = new String("FOO BAR BAZ ".ToList().Select((x, i) => x = (i % 2 == 0 ? ++x : --x )).ToArray());
which outputs
GNPC#SC#[
I need to split this string into two parts so that my output will be
`"GPCSC[", "N##"`
I couldn't sort it out yet.
Any solution advice on this?
PS: I am looking for simple solution, not bunch of lines of codes
Fiddle Link : https://dotnetfiddle.net/ml8bOC
You might need to use GroupBy:
string str = "GNPC#SC#[";
var groups = str.Select((v, i) => new { Group = i % 3, Ch = v })
.GroupBy(item => item.Group == 1)
.Select(group => string.Join("", group.Select(item => item.Ch)))
.ToList();
// groups: ["GPCSC[", "N##"]
Is it possible to split each string (containing 2 words) in a list, then compare if both words are the same and count that occurrences using Linq? For example:
Let's say I have a list containing
list[0] = "bla bla";
list[1] = "bla heh";
list[2] = "heh heh";
The output of count should be 2 in this case.
my attempt so far:
var count = lst.Count(c => c.Split(.......)....
can't get past this.
Any help would be greatly appreciated.
list.Select(c => c.Split(' ')).Count(y => y.Length >= 2 && y[0] == y[1]);
You can utilise the Select clause then the Count like so:
int count = myList.Select(s => s.Split(' '))
.Count(a => a[0] == a[1]);
or you can use Count only like this:
int count = myList.Count(s => s.Substring(0, s.IndexOf(' ')) ==
s.Substring(s.IndexOf(' ') + 1));
With the new Span<T> value type (see this article) from nuget package 'System.Memory' you can do this without any unnecessary allocations:
int count = input.Count(x =>
{
int index = x.IndexOf(' ');
if (index < 1 || index == x.Length - 1) return false;
var span = x.AsSpan();
return span.Slice(start:0, length:index) // no allocation
.SequenceEqual(
span.Slice(start: index + 1)); // no allocation
});
Same as the others, but if you would have 3 or more words it would check them against eachother and only count the arrays that have the same words everywhere.
var result = test.Select(x => x.Split(' ')).Count(x => x.All(y => x[0] == y));
I have Licence plate numbers which I return to UI and I want them ordered in asc order:
So let's say the input is as below:
1/12/13/2
1/12/11/3
1/12/12/2
1/12/12/1
My expected output is:
1/12/11/3
1/12/12/1
1/12/12/2
1/12/13/2
My current code which is working to do this is:
var orderedData = allLicenceNumbers
.OrderBy(x => x.LicenceNumber.Length)
.ThenBy(x => x.LicenceNumber)
.ToList();
However for another input sample as below:
4/032/004/2
4/032/004/9
4/032/004/3/A
4/032/004/3/B
4/032/004/11
I am getting the data returned as:
4/032/004/2
4/032/004/9
4/032/004/11
4/032/004/3/A
4/032/004/3/B
when what I need is:
4/032/004/2
4/032/004/3/A
4/032/004/3/B
4/032/004/9
4/032/004/11
Is there a better way I can order this simply to give correct result in both sample inputs or will I need to write a custom sort?
EDIT
It wont always be the same element on the string.
This could be example input:
2/3/5/1/A
1/4/6/7
1/3/8/9/B
1/3/8/9/A
1/5/6/7
Expected output would be:
1/3/8/9/A
1/3/8/9/B
1/4/6/7
1/5/6/7
2/3/5/1/A
You should split your numbers and compare each part with each other. Compare numbers by value and strings lexicographically.
var licenceNumbers = new[]
{
"4/032/004/2",
"4/032/004/9",
"4/032/004/3",
"4/032/004/3/A",
"4/032/004/3/B",
"4/032/004/11"
};
var ordered = licenceNumbers
.Select(n => n.Split(new[] { '/' }))
.OrderBy(t => t, new LicenceNumberComparer())
.Select(t => String.Join("/", t));
Using the following comparer:
public class LicenceNumberComparer: IComparer<string[]>
{
public int Compare(string[] a, string[] b)
{
var len = Math.Min(a.Length, b.Length);
for(var i = 0; i < len; i++)
{
var aIsNum = int.TryParse(a[i], out int aNum);
var bIsNum = int.TryParse(b[i], out int bNum);
if (aIsNum && bIsNum)
{
if (aNum != bNum)
{
return aNum - bNum;
}
}
else
{
var strCompare = String.Compare(a[i], b[i]);
if (strCompare != 0)
{
return strCompare;
}
}
}
return a.Length - b.Length;
}
}
If we can assume that
Number plate constist of several (one or more) parts separated by '/', e.g. 4, 032, 004, 2
Each part is not longer than some constant value (3 in the code below)
Each part consist of either digits (e.g. 4, 032) or non-digits (e.g. A, B)
We can just PadLeft each number plate's digit part with 0 in order to compare not "3" and "11" (and get "3" > "11") but padded "003" < "011":
var source = new string[] {
"4/032/004/2",
"4/032/004/9",
"4/032/004/3/A",
"4/032/004/3/B",
"4/032/004/11",
};
var ordered = source
.OrderBy(item => string.Concat(item
.Split('/') // for each part
.Select(part => part.All(char.IsDigit) // we either
? part.PadLeft(3, '0') // Pad digit parts e.g. 3 -> 003, 11 -> 011
: part))); // ..or leave it as is
Console.WriteLine(string.Join(Environment.NewLine, ordered));
Outcome:
4/032/004/2
4/032/004/3/A
4/032/004/3/B
4/032/004/9
4/032/004/11
You seem to be wanting to sort on the fourth element of the string (delimited by /) in numeric rather than string mode.. ?
You can make a lambda more involved/multi-statement by putting it like any other method code block, in { }
var orderedData = allLicenceNumbers
.OrderBy(x =>
{
var t = x.Split('/');
if(t.Length<4)
return -1;
else{
int o = -1;
int.TryParse(t[3], out o);
return o;
}
)
.ToList();
If you're after sorting on more elements of the string, you might want to look at some alternative logic, perhaps if the first part of the string will always be in the form N/NNN/NNN/??/?, then do:
var orderedData = allLicenceNumbers
.OrderBy(w => w.Remove(9)) //the first 9 are always in the form N/NNN/NNN
.ThenBy(x => //then there's maybe a number that should be parsed
{
var t = x.Split('/');
if(t.Length<4)
return -1;
else{
int o = -1;
int.TryParse(t[3], out o);
return o;
}
)
.ThenBy(y => y.Substring(y.LastIndexOf('/'))) //then there's maybe A or B..
.ToList();
Ultimately, it seems that more and more outliers will be thrown into the mix, so you're just going to have to keep inventing rules to sort with..
Either that or change your strings to standardize everything (int an NNN/NNN/NNN/NNN/NNA format for example), and then sort as strings..
var orderedData = allLicenceNumbers
.OrderBy(x =>
{
var t = x.Split('/');
for(int i = 0; i < t.Length; i++) //make all elements in the form NNN
{
t[i] = "000" + t[i];
t[i] = t[i].Substring(t[i].Length - 3);
}
return string.Join(t, "/");
}
)
.ToList();
Mmm.. nasty!
I am using a LINQ subquery to obtain all the words of minimum length in an array.
I want to do it using Lambda Expression.
var names = new[] { "Tom", "Dick", "Harry", "Mary", "Jay" }.AsQueryable();
(
from n in names
where n.Length == names.Min (n2 => n2.Length)
select n
)
Output :
Tom , Jay
Thanks,
Prakhar
This would work:
var minNames = names.Where(s => s.Length == names.Min(n=>n.Length));
But it evaluates the min length for every name in list (O(n*n) complexity), therefore this would be better:
var min = names.Min(s => s.Length); //calc. this only once
var minNames = names.Where(s => s.Length == min);
The question to me seems a little bit vague, but is this what you're looking for?
names.Where (x => x.Length == names.Min (n2 => n2.Length));
This should help you:
var minNames = names.Where(c => c.Length == names.Min(n => n.Length))
.ToArray();
I have a list containing integer or string-integer
like this
TagNo FTerminal
1000 1
1000 5
1000 2S6
how can i get the result like this
TagNo FTerminal
1000 1
5
6
I have this , but definately it gives me error on 2s6.
how can i change it to cover all?
var terminalList = sourceLists.Where(t => t.TagNo == tagList)
.Where(t=>t.FromTerminal.Length>0)
.Select(t => int.Parse(t.FromTerminal))
.OrderBy(t=>t)
.ToList();
Instead of using int.Parse in your LINQ statement, you need to write your own function.
Something like this:
int parseTerminal(string input) {
int result = -1;
if (!int.TryParse(input, out result)) {
result = -99;
}
return result;
}
That would make your LINQ to
var terminalList = sourceLists
.Where( t => t.TagNo == tagList && t.FromTerminal.Length > 0 )
.Select( t => parseTerminak(t.FromTerminal) )
.OrderBy( t=>t )
.ToList();
Result:
TagNo FTerminal
1000 -99
1
5
You need to handle the special case where FromTerminal is not a number yourself.
A naive implementation of the requirement one could think of is something like this:
int parseTerminal(string input) {
int result = -1;
if (!int.TryParse(input, out result)) {
var temporaryString = string.Empty;
var lastInt = -1;
input.ToList().ForEach( aChar => {
if ( aChar >= '0' && aChar <= '9' ) {
temporaryString += aChar;
} else {
if ( temporaryString.Length >= 0 ) {
int.TryParse( temporaryString, out lastInt );
temporaryString = string.Empty;
}
}
} );
if ( temporaryString.Length >= 0 ) {
if (!int.TryParse( temporaryString, out lastInt )) {
lastInt = -98;
}
}
result = lastInt;
}
return result;
}
Note: I would not consider this production ready and you should think about edge cases.
Without knowing much about your data structure I have written a code using some system types.
var tuples = new List<Tuple<int, string>>
{
new Tuple<int, string>(1000, "1"),
new Tuple<int, string>(1000, "5"),
new Tuple<int, string>(1000,"2s6")
};
var enumerable = tuples.GroupBy(t => t.Item1).
Select(g => new Tuple<int, List<int>>(g.Key, g.Select(e => int.Parse(Regex.Match(e.Item2, #"(?<=(\D|^))\d+(?=\D*$)").Value)).ToList()));
The error occurs because you try to handle the string '2S6' as an integer, using int.Parse. This would naturally cause an exception.
I would suggest following another approach, using regular expressions. I think regular expressions are better solution since the data you ask come after string manipulation of the already retrieved query results.
Using regular expressions to do this kind of staff, would make it also easier for you to maintain in the future. Think of the case, that in a week you don't want to retrieve the last digit of the string, but the second digit of the string.
You can use this online regular expression tester to test your regular expression. I suppose the regular expression \d(?!.*\d) would be a good choice, since it returns the last digit.
This article is a good guide in using regular expressions in .NET, including examples.
Hope I helped!
if last symbol always is int you may change your code like this
var terminalList = sourceLists.Where(t => t.TagNo == tagList)
.Where(t=>t.FromTerminal.Length>0)
.Select(t => int.Parse(t.FromTerminal.Last()))
.OrderBy(t=>t)
.ToList();
UPDATE
if last not only one numer that can use regex like this
var terminalList = sourceLists.Where(t => t.TagNo == tagList)
.Where(t=>t.FromTerminal.Length>0)
.Select(t => int.Parse(Regex.Match(t.FromTerminal, #"(\d+)$").Groups[1].Value))
.OrderBy(t=>t)
.ToList();
I am quiet confused what do you wants ... as your picture says you wants the combination of TagNO and FTerminal and in the other hand your query says you wants only FTerminals in certain order ..
Now if you wants the first one then
void Abc(int tagList)
{
var sourceLists = new List<Demo>
{
new Demo { FTerminal = "200", TagNo = 1000 },
new Demo { FTerminal = "300", TagNo = 1000 },
new Demo { FTerminal = "400", TagNo = 1000 }
};
var terminalList = sourceLists
.Where(t => t.TagNo == tagList && t.FTerminal.Length > 0)
.OrderBy(i=>i.FTerminal).GroupBy(i=>i.TagNo);
}
And the second one
void Abc(int tagList)
{
var sourceLists = new List<Demo>
{
new Demo { FTerminal = "200", TagNo = 1000 },
new Demo { FTerminal = "300", TagNo = 1000 },
new Demo { FTerminal = "400", TagNo = 1000 }
};
var terminalList =
from Demo d in sourceLists
where d.TagNo == tagList
let number = int.Parse(d.FTerminal)
orderby number ascending
select number).ToList();
}
But till if you did not get the your desired answer then please knock!!!!
Hmm ... instead of jumping through hoops, just use IsInt ... problem solved ...
:)
var terminalList = sourceLists.Where(t => t.TagNo == tagList)
.Where(t=>t.FromTerminal.Length>0)
.Where(t => t.FromTerminal.IsInt() )
.Select(t => int.Parse(t.FromTerminal))
.OrderBy(t=>t)
.ToList();
(So, just added this condition .Where(t => t.FromTerminal.IsInt() ) to your selection process)