If number have no value it should placed last(LINQ) - c#

I am using linq to sql and my code looks like this:
var agendaLists =
dataContext.view_Agendas.Where(m => m.MeetingID == meetingID)
.OrderBy(n => n.Number)
.ThenBy(n => n.CaseNumber)
.ThenByDescending(s => s.MainYN)
.ToList();
So basicly our "n.Number" is the order number. everything works fine its sorted 1,2,3,4 etc which is correct. but if a object have no value in n.Number it will be displayed in the top but I want it to be placed last.
Today it sorts like this, lets say we get 4 objects back:
Null, 1, 2, 3
I want it sorted like this:
1, 2, 3, Null
Any kind of help is appreciated.

try the following:
n => n.Number ?? int.MaxValue
I dont remember if you actually have to check for DBNull, but it would be the same.

You can use this approach, presuming Number is an int?:
var agendaLists = dataContext.view_Agendas
.Where(m => m.MeetingID == meetingID)
.OrderBy(m => n.Number.HasValue ? 0 : 1)
.ThenBy(n => n.Number)
.ThenBy(n => n.CaseNumber)
.ThenByDescending(s => s.MainYN)
.ToList();

Related

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

How to concatenate result of GroupBy using Linq

Let say you have list of items and you want to partition them, make operation on one partition and concatenate partitions back into list.
For example there is list of numbers and I want to partition them by parity, then reverse odds and concatenate with evens. [1,2,3,4,5,6,7,8] -> [7,5,3,1,2,4,6,8]
Sounds simple, but I've got stuck on merging back two groups. How would you do it with LINQ?
IEnumerable<int> result = Enumerable.Range(0, 1000)
.GroupBy(i => i % 2)
.Select(p => p.Key == 1 ? p.Reverse() : p)
.??? // need to concatenate
Edit
[1,2,3] is the representation of array which I want to get as the result, not output, sorry if I confused you by that.
The GroupBy method returns an IEnumerable<IGrouping<TKey, TSource>>. As IGrouping implements IEnumerable, you can use SelectMany to concatenate multiple IEnumerable<T> instances into one.
Enumerable.Range(0, 1000)
.GroupBy(i => i % 2)
.Select(p => p.Key == 1 ? p.Reverse() : p)
.OrderByDescending(p => p.Key)
.SelectMany(p => p);
There are a few ways to achieve this,
so if we start with your function
Enumerable.Range(0, 1000)
.GroupBy(i => i % 2)
.Select(p => p.Key == 1 ? p.Reverse() : p)
you could then use an Aggregate
.Aggregate((aggrgate,enumerable)=>aggrgate.Concat(enumerable))
this will then go though your list of results and concat them all into a collection and return it, you just need to make sure that aggrgate and enumerable are the same type in this case a IEnumerable<int>
another would be to call SelectMany()
.SelectMany(enumerable=>enumerable)
this then likewise pulls all the enumerables together into a single enumerable, again you need to ensure the types are IEnumerable<int>
other options would be to hard code the keys as Tim is suggesting or pull out of linq and use a loop
You could use this approach using a Lookup<TKey, TElement>:
var evenOddLookup = numbers.ToLookup(i => i % 2);
string result = String.Join(",", evenOddLookup[1].Reverse().Concat(evenOddLookup[0]));
If you don't want a string but an int[] as result:
int[] result = evenOddLookup[1].Reverse().Concat(evenOddLookup[0]).ToArray();
You could do something like this.
var number = string.Join(",",
Enumerable.Range(0, 1000)
.GroupBy(i => i % 2) // Separate even/odd numbers
.OrderByDescending(x=>x.Key) // Sort to bring odd numbers first.
.SelectMany(x=> x.Key ==1? // Sort elements based on even or odd.
x.OrderByDescending(s=>s)
: x.Where(s=> s!=0).OrderBy(s=>s))
.ToArray());
string output = string.Format("[{0}]", number);
Check this Demo
Just use OrderBy like this:
List<int> arr = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
var result = arr.OrderBy(i => i % 2 == 0 ? 1 : 0)
.ThenBy(i => i % 2 == 0 ? i : int.MaxValue)
.ThenByDescending(i => i);
This should give you your desired result as you want:
[1,2,3,4,5,6,7,8] will be converted into [7,5,3,1,2,4,6,8]

How to OrderBy using Linq with missing elements?

Usually when using Linq I would usually filter out empty/null records using Where or similar. However, I need to order a List on multiple criteria and retain all items in the list but in order.
The following works only when .Dimension1.Count > 0 for all items in the list
var orderedList = mList.Elements
.OrderBy(x => x.Property1)
.ThenBy(x => x.Property2)
.ThenBy(x => x.Property3.Dimension1[0].Value2)
.ToList();
If any of the elements have Dimension1.Count == 0 then I get error:
'Index was out of range. Must be non-negative and less than the size of the collection.'
Which is expected as the array is not dimensioned.
Is there a way to make this work when the list contains items which have .Dimension1.Count = 0?
Note that Dimension1[0].Value2 is type double.
You can do something like this:
var orderedList = mList.Elements.OrderBy(x => x.Property1)
.ThenBy(x => x.Property2)
.ThenBy(x => x.Property3.Dimension1.Count == 0
? -1
: x.Property3.Dimension1[0].Value2)
.ToList();
I am assuming here that Value2 is an integer. If it is a string for example, you can use null instead of -1.
The idea is that you use a special value when Count is 0.
You don't mention whether you're using, for example, Entity Framework. It's doubtful this would convert to a valid SQL statement (it might..), but this may be worth a try in your case.
.ThenBy(x => x.Property3.Dimension1[0].Count > 0 ? x.Property3.Dimension1[0].Value2 : -1)
I'm assuming Value2 is always > 0, so that any records missing values should be assigned -1 and be pushed further down the list. If that's not the case, you may need to change -1 to something more appropriate to your situation.
You can just provide a default value for the second ThenBy.
.ThenBy(x => x.Property3.Dimension1.Any()
? x.Property3.Dimension1[0].Value2
: // Some default value for the type of Value2.
// Go high or low depending on the type to put
// them at the top or bottom of the list
);
if you want to be sure that will not be null exception for Dimensions1 array:
.Then(x => (x.Property3.Dimensions1 ?? new object[0]).Count > 0 ? x.Property3.Dimensions1[0].Value2 : -1)
There is a dedicated LINQ method for that, DefaultIfEmpty, which
Returns the elements of the specified sequence or the specified value
in a singleton collection if the sequence is empty.
var orderedList = mList.Elements
.OrderBy(x => x.Property1)
.ThenBy(x => x.Property2)
.ThenBy(x => x.Property3.Dimension1.DefaultIfEmpty(someDefaultValue).ElementAt(0).Value2)
.ToList();

Order By on the Basis of Integer present in string

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.

Format Data using Linq

I have a data set something like this:
2,Black
2,Blue
2,Red
1,Small
1,Medium
I need to convert this into the following:
2_0
2_1
2_2
1_0
1_1
The LINQ query I have at the moment uses an index for the second number, however it doesn't reset to 0 when changing from 2_ to 1_. I've tried using a GroupBy, but I can't get the results I need - can anyone help?
IEnumerable<string> output = input
.GroupBy(i => i.Num)
.SelectMany(grp => grp.Select((item, idx) => string.Format("{0}_{1}", grp.Key, idx)));
You can group by the number and use the version of Select() that provides the index:
var result = data.GroupBy(x => x.Number,
(key, g) => g.Select((_, i) => string.Format("{0}_{1}", key, i)))
.SelectMany(x => x);
Note that this might behave differently than you'd expect if the same numbers aren't contiguous: e.g, 2, 2, 2, 1, 1, 2, 2.

Categories

Resources