LINQ - sort on multiple columns - linq

I have a collection with the values below:
ID | OtherId
1 0
2 0
3 1
3 2
3 3
4 1
4 2
4 3
5 0
6 0
Is there a way to get the output in the below sorted way using LINQ?
ID | OtherId
1 0
2 0
3 1
4 1
3 2
4 2
3 3
4 3
5 0
6 0

You'll need to use OrderBy and ThenBy:
I haven't tried this, but because your last 2 rows are "out of order" you'll need to first order by (not) specific values, then by columns. It'll be something like this:
var sorted = table
.Where(x => x.SomeColumn == someValue)
.OrderBy(x => x.ID != 5)
.ThenBy(x => x.ID != 6)
.ThenBy(x => x.OtherId)
.ThenBy(x => x.ID)
.ToList();
If you don't want to sort by specific values, you'll need to do 2 queries and union:
var valueAtWhichSortingLogicChanges = 4;
var sorted = table
.Where(x => x.ID <= valueAtWhichSortingLogicChanges)
.OrderBy(x => x.OtherId)
.ThenBy(x => x.ID)
.ToList();
sorted.AddRange(table
.Where(x => x.ID > valueAtWhichSortingLogicChanges)
.OrderBy(x => x.OtherId)
.ThenBy(x => x.ID)
.ToList());

Related

linq query to list that adds position of result

I have the linq query that produces the result below:
var result = from x in model.SITEs
where x.SiteId == homeSite
select new { x.SiteId,
x.SiteAlloc1,
x.SiteAlloc2,
x.SiteAlloc3,
x.SiteAlloc4 });
SiteId SiteAlloc1 SiteAlloc2 SiteAlloc3 SiteAlloc4
======================================================
1 5 3 2 4
But what I need is something more like this, where the Rank is the position of the SiteId in the result.
SiteId Rank
==================
1 1
2 4
3 3
4 5
5 2
var result = from x in model.SITEs
where x.SiteId == homeSite
select new { x.SiteId,
x.SiteAlloc1,
x.SiteAlloc2,
x.SiteAlloc3,
x.SiteAlloc4 }).
Select((t,u) => new {
SiteId = t.SiteId,
SiteAlloc1 = t.SiteAlloc1,
SiteAlloc2 = t.SiteAlloc2,
SiteAlloc3 = t.SiteAlloc3,
SiteAlloc4 = t.SiteAlloc4,
Rank = u + 1));
Where u is the index (0 based, that's why I added 1), or in your case the rank and t is the selected object

simple join of three tables in LINQ

i dont understand linq properly. i dont know why.
i have three tables.
1)TillTable
tillId, tillName
1 w1
2 w2
3 w3
4 w4
2)TillDepartment
tillDeptId, tillId, deptId, isPart
1 1 5 1
2 1 7 0
3 1 8 0
4 1 9 0
5 2 5 0
6 2 7 0
7 2 8 0
8 2 9 0
9 3 5 0
10 3 7 1
11 3 8 0
12 3 9 0
13 4 5 0
14 4 7 0
15 4 9 0 so on....
3) departmentTable
deptId, deptName
5 Science
7 Commerce
8 history
9 English so on....
now using linq or lambda exp i want to display following result,
tillId, tillName, deptName
1 w1 science
2 w2 no dept
3 w3 commerce
4 w4 no dept so on...
i hope for linq master its damn easy task....
help me to get it resolved....
if isPart column is 1 then in result set deptName should be displayed else 'no dept' ....
associative table has multiple entries for tillId.....
its requirement so strictly follow this scenario only.....
i hope its clr ......
Try this:-
var result = (from tt in db.tillTables
join td in db.tillDepts
on tt.tillId equals td.tillId
join dt in db.departmentTable
on td.deptid equals dt.deptId
select new
{
tillId = tt.tillId,
tillName = tt.tillName,
deptName = td.isPart == 1 ? dt.deptName : null
}).GroupBy(x => x.tillId)
.Select(x =>
{
var orderedDeptRecord = x.OrderByDescending(z => z.deptName).FirstOrDefault();
return new
{
tillId = x.Key,
tillName = orderedDeptRecord.tillName,
deptName = orderedDeptRecord.deptName
};
});
Outer join to single record,then, the foreign key to the deptName is the select clause with ternary operator (gets translated to "case")
from t in TillTable
join tds in TillDepartment on new { t.tillId,isPart=1}
equals new { td.tillId, isPart } into tdOuter
from td in tdOuter.DefaultIfEmpty().Take(1)
select new { t.tillId, t.tillName,
deptName=((td==null)? "no dept" :
(from dt in departmentTable
where dt.deptId == td.deptId
select deptName ) ) }

Replacing values with LINQ in a list with the previous value matching some criterion

I have produced a list of two values.
Id Value
10 0
11 1
12 2
13 3
14 -1
15 -1
16 6
17 -1
18 8
19 9
I would like all -1 values to be replaced with the previous not -1 value such that I end up with the list
Id Value
10 0
11 1
12 2
13 3
14 3
15 3
16 6
17 6
18 8
19 9
Is there a cool way of doing this in LINQ?
If you need to access previous elements with LINQ a loop might be more appropriate (readable,efficient).
However (Obj is your class):
list = list.Select((x, index) =>
{
if (index != 0 && x.Value == -1)
{
x.Value = list.Take(index)
.Where(xPrev => xPrev.Value != -1)
.Select(xPrev => xPrev.Value)
.DefaultIfEmpty(int.MinValue)
.Last();
}
return x;
}).ToList();
Note that this does not ensure that you get a different value than -1 since i'm just selecting the previous.
Why not:
var lastValue = -1;
foreach (var item in items)
{
if (item.Value == -1)
{
item.Value = lastValue;
}
else
{
lastValue = item.Value;
}
}
Yet another variant - use Aggregate extension like this
list = list.Aggregate(new List<YourObjectClass>(), (a, b) =>
{
if (b.Value == -1 && a.Count>0)
b.Value = a.Last().Value;
a.Add(b);
return a;
});

get the sum of the maxs group by

I have this table
EquipmentId Value Date
1 2 11/04/2013
1 1 11/04/2013
2 3 11/04/2013
2 2 10/04/2013
2 5 10/04/2013
3 1 10/04/2013
3 3 11/04/2013
I want to group these items by date, and have a dictionary with the date as a key and the sum of the maxs of the all equipments values in that day
the result would be like this
[10/04/2013: 6] // 6 = 5 (as the max of values of the the equipmetId 2) + 1 (as the max of values of the the equipmetId 3)
[11/04/2013: 5] // 5 = 2(as the max of values of the the equipmetId 1) + 3(as the max of values of the the equipmetId 3)
I managed to make the query to get this without the sum, meaning for only one equipment.
var consumptionValues = (from c in context.ConsumptionSet
join pi in context.PropertiesInstanceSet on c.PropertiesInstanceID equals pi.PropertiesInstanceID
join ep in context.EquipmentPropertiesSet on pi.EquipmentPropertiesID equals ep.EquipmentPropertiesID
join e in context.EquipmentSet on ep.EquipmentID equals e.EquipmentID
where (e.EquipmentID == equipmentId && pi.ProprietesName == ProprietesName.Energy && c.Date <= DateTime.Now && c.Date >= firstDayDate)
group c by SqlFunctions.DatePart("weekday", c.Date) into grp
select new
{
dayOfWeek = (DayOfWeek)grp.Key.Value - 1,
value = grp.Max(c => c.Value),
}).ToDictionary(c => c.dayOfWeek.ToString(), c => c.value);
It's the complete query with all the joins, in the example I just gave a simplified example.
Is it possible to do this in one single query ?
I have to say I'm not sure it will work, but you should give it a shot:
var consumptionValues = (from c in context.ConsumptionSet
join pi in context.PropertiesInstanceSet on c.PropertiesInstanceID equals pi.PropertiesInstanceID
join ep in context.EquipmentPropertiesSet on pi.EquipmentPropertiesID equals ep.EquipmentPropertiesID
join e in context.EquipmentSet on ep.EquipmentID equals e.EquipmentID
where (e.EquipmentID == equipmentId && pi.ProprietesName == ProprietesName.Energy && c.Date <= DateTime.Now && c.Date >= firstDayDate)
group new { c, e } by SqlFunctions.DatePart("weekday", c.Date) into grp
select new
{
dayOfWeek = (DayOfWeek)grp.Key.Value - 1,
value = grp.GroupBy(i => i.e.EquipmentID).Sum(g => g.Max(i => i.c.Value)),
}).ToDictionary(c => c.dayOfWeek.ToString(), c => c.value);

Linq OrderByDescending but keep zero value first

I have a collection of integers that I want to order by descending value, with the exception of keeping a 0 value as first in the list.
For example:
0,1,2,3,4,5,6,7,8
Should result in:
0,8,7,6,5,4,3,2,1
Thanks!
var input = new [] {0,1,2,3,4,5,6,7,8};
Two sortings which work with both negative and positive numbers:
var result = input.OrderBy(i => i == 0? 0 : 1).ThenByDescending(i => i);
or this if all your numbers are non-negative:
var result = input.OrderByDescending(i => i == 0? int.MaxValue : i);
or some really weird solution if you have both negative and positive numbers but you don't want to sort twice (as I'm doing in first solution):
var result = input
.GroupBy(i => i == 0 ? 0 : 1)
.OrderBy(g => g.Key)
.Select(g => g.Key == 0 ? g : g.OrderByDescending(i => i)
.SelectMany(g => g);

Resources