Have a list with deviceIds and corresponding actions to be taken on device.
var results= new List<Result>
{
new Result{ DeviceId= 1, ActionType = 1 },
new Result{ DeviceId= 1, ActionType = 2 },
new Result{ DeviceId= 1, ActionType = 3 },
new Result{ DeviceId= 2, ActionType = 1 },
new Result{ DeviceId= 3, ActionType = 1 },
new Result{ DeviceId= 4, ActionType = 1 },
new Result{ DeviceId= 5, ActionType = 1 },
new Result{ DeviceId= 6, ActionType = 1 },
new Result{ DeviceId= 6, ActionType = 2 },
};
How do I filter deviceIds unique in the list(no DeviceId 1), and assign it back to var "results"
results = List<Result>
{
new Result{ DeviceId= 2, ActionType = 1 },
new Result{ DeviceId= 3, ActionType = 1 },
new Result{ DeviceId= 4, ActionType = 1 },
new Result{ DeviceId= 5, ActionType = 1 },
};
Have tried using groupby and couldn't move forward
results = from result in results
group result by result.DeviceId
into groupedResultsByDevice
where groupedResultsByDevice.Count() == 1
select ????
Besides answer with query syntax, in method syntax LINQ query it will be:
results = results.GroupBy(r => r.DeviceId)
.Where(g => g.Key != 1 && g.Count() == 1)
.Select(g => g.First())
.ToList();
After grouping you can select the first (and only element of the group):
results = from result in results
group result by result.DeviceId
into groupedResultsByDevice
where groupedResultsByDevice.Count() == 1
select groupedResultsByDevice.First(); // <---
results = from r in results
group r by r.DeviceId into g
where g.Count() == 1
select g.First()
You can make it a little bit more efficient replacing g.Count() with !g.Skip(1).Any():
results = from r in results
group r by r.DeviceId into g
where !g.Skip(1).Any()
select g.First()
It will return false as soon as second element is found, instead of counting all items in the group.
Check this out
public static void Main()
{
var results = new List<Result>
{
new Result {DeviceId = 1, ActionType = 1},
new Result {DeviceId = 1, ActionType = 2},
new Result {DeviceId = 1, ActionType = 3},
new Result {DeviceId = 2, ActionType = 1},
new Result {DeviceId = 3, ActionType = 1},
new Result {DeviceId = 4, ActionType = 1},
new Result {DeviceId = 5, ActionType = 1},
new Result {DeviceId = 6, ActionType = 1},
new Result {DeviceId = 6, ActionType = 2},
};
List<Result> result = results
.GroupBy(x => x.DeviceId)
.Where(x => x.Count() == 1)
.SelectMany(x => x)
.Distinct()
.ToList();
result.ForEach(Console.WriteLine);
Console.ReadLine();
}
public sealed class Result : IEqualityComparer<Result>
{
public int DeviceId { get; set; }
public int ActionType { get; set; }
public bool Equals(Result x, Result y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null)) return false;
if (ReferenceEquals(y, null)) return false;
if (x.GetType() != y.GetType()) return false;
return x.DeviceId == y.DeviceId && x.ActionType == y.ActionType;
}
public int GetHashCode(Result obj)
{
unchecked
{
return (obj.DeviceId*397) ^ obj.ActionType;
}
}
public override string ToString()
{
return string.Format("DeviceId: {0}, ActionType: {1}", DeviceId, ActionType);
}
}
Result output:
DeviceId: 2, ActionType: 1
DeviceId: 3, ActionType: 1
DeviceId: 4, ActionType: 1
DeviceId: 5, ActionType: 1
Related
Suffering sadly from brain fade. I have the following scenario:
void Main()
{
List<CaseBase> caseList = new List<UserQuery.CaseBase>();
caseList.Add(new CaseBase() {CaseID = 1, CaseSequence = 1, CaseStatus = 1});
caseList.Add(new CaseBase() {CaseID = 1, CaseSequence = 2, CaseStatus = 2});
caseList.Add(new CaseBase() {CaseID = 2, CaseSequence = 1, CaseStatus = 1});
var cases = caseList.Where(x => new List<int> {2}.Contains(x.CaseStatus));
}
// Define other methods and classes here
public class CaseBase
{
public int CaseID {get;set;}
public int CaseSequence {get;set;}
public int CaseStatus {get;set;}
}
Which returns the expected
CaseID
CaseSequence
CaseStatus
1
2
2
What I want are all cases with the same ID where one of them has a status of 2.
CaseID
CaseSequence
CaseStatus
1
1
1
1
2
2
Which should be simple but I'm struggling for a simple solution.
There are a couple of ways to proceed:
You can combine the cases by CaseID and select the matching groups and then break them apart:
var cases = caseList
.GroupBy(c => c.CaseID)
.Where(cg => cg.Any(c => new List<int> { 2 }.Contains(c.CaseStatus)))
.SelectMany(cg => cg);
You can find the desired CaseIDs and then get all matching cases:
var wantedCaseIDs = caseList
.Where(c => new List<int> { 2 }.Contains(c.CaseStatus))
.Select(c => c.CaseID)
.ToHashSet();
var cases = caseList.Where(c => wantedCaseIDs.Contains(c.CaseID));
Or you might want to do it like this:
void Main()
{
List<CaseBase> caseList = new List<UserQuery.CaseBase>();
caseList.Add(new CaseBase() { CaseID = 1, CaseSequence = 1, CaseStatus = 1 });
caseList.Add(new CaseBase() { CaseID = 1, CaseSequence = 2, CaseStatus = 2 });
caseList.Add(new CaseBase() { CaseID = 2, CaseSequence = 1, CaseStatus = 1 });
var cases = caseList.Where(x => new List<int> { 2 }.Contains(x.CaseStatus))
.Join(caseList,x => x.CaseID,y => y.CaseID,(x,y) => new {x,y})
.Select(z => z.y)
.Dump();
}
I have array:
OrderProduct[] OrderProductsOrder = new OrderProduct[] {
new OrderProduct { OrderID = 1, ProductID = 2, OrderCustomerID = 1 },
new OrderProduct { OrderID = 2, ProductID = 1, OrderCustomerID = 1 },
new OrderProduct { OrderID = 1, ProductID = 3, OrderCustomerID = 1 },
new OrderProduct { OrderID = 2, ProductID = 1, OrderCustomerID = 2 },
new OrderProduct { OrderID = 1, ProductID = 2, OrderCustomerID = 3 },
new OrderProduct { OrderID = 2, ProductID = 1, OrderCustomerID = 3 }};
How to split this array to three arrays, order by CustomerID, using linq.
Result should be this three arrays:
OrderProduct[] Customer1Order = new OrderProduct[] {
new OrderProduct { OrderID = 1, ProductID = 2, OrderCustomerID = 1 },
new OrderProduct { OrderID = 2, ProductID = 1, OrderCustomerID = 1 },
new OrderProduct { OrderID = 1, ProductID = 3, OrderCustomerID = 1 }};
OrderProduct[] Customer2Order = new OrderProduct[]
{new OrderProduct { OrderID = 2, ProductID = 1, OrderCustomerID = 2 }};
OrderProduct[] Customer3Order = new OrderProduct[] {
new OrderProduct { OrderID = 1, ProductID = 2, OrderCustomerID = 3 },
new OrderProduct { OrderID = 2, ProductID = 1, OrderCustomerID = 3 }};
Edited, removed the GroupBy() suggestion as it was redundant (courtesy of Innat3)
No reason to use GroupBy() at all, just use Where.
OrderProduct[] Customer1Order = OrderProductsOrder.Where(o => o.OrderCustomerID == 1).ToArray();
OrderProduct[] Customer2Order = OrderProductsOrder.Where(o => o.OrderCustomerID == 2).ToArray();
OrderProduct[] Customer3Order = OrderProductsOrder.Where(o => o.OrderCustomerID == 3).ToArray();
Start by grouping the entries by OrderCustomerID, and constructing an array from each group. After that, add groups to a dictionary:
var byCustId = OrderProductsOrder
.GroupBy(p => p.OrderCustomerID)
.ToDictionary(g => g.Key, g => g.ToArray());
Now you can grab individual arrays with TryGetValue or operator []:
OrderProduct[] customer2Order;
if (byCustId.TryGetValue(2, out customer2Order) {
... // Use customer2Order array
}
I have an object (Details) which is having employee details. That object has properties (sal and Level).
I just want to get the sum of sal for each and every Level.
foreach (var emp in Details)
{
int empsal=emp.sal;
int empLevel= emp.Level;
}
example: I want the sum of all employee sal into different variables based on the levels.
var sumByLevels = Details.GroupBy(employee => employee.Level)
.ToDictionary(g => g.Key, g => g.Sum(x => x.sal));
Now assume you want to get total salary of level 1:
var result = sumByLevels[1];
You can do that using LINQ's GroupBy method:
Dictionary<int, int> sumsByLevel =
Details.GroupBy(employee => employee.Level, employee => employee.sal).
ToDictionary(group => group.Key, group => group.Sum());
This results in a dictionary with the Levels as keys and the sum of the corresponding sals as value.
The first answer is good. For example you have class:
public class Details
{
public int Level { get; set; }
public int Sal { get; set; }
}
and you have data:
var data = new List<Details>()
{
new Details() {Level = 1, Sal = 1},
new Details() {Level = 1, Sal = 1},
new Details() {Level = 1, Sal = 1},
new Details() {Level = 2, Sal = 2},
new Details() {Level = 2, Sal = 2},
new Details() {Level = 3, Sal = 3},
new Details() {Level = 3, Sal = 3},
new Details() {Level = 3, Sal = 3},
};
if we calculate, we get:
level(1) = 3
level(2) = 4
level(3) = 9
Solution one (good):
var sum = data.GroupBy(x => x.Level).ToDictionary(x => x.Key, a => a.Sum(b => b.Sal));
Console.WriteLine(sum[1]); // result 3
Console.WriteLine(sum[2]); // result 4
Console.WriteLine(sum[3]); // result 9
Solution two (not very good):
public int GetSumOfLevel(int level, List<Details> details)
{
return details.Where(x => x.Level == level).Sum(x => x.Sal);
}
...
Console.WriteLine(GetSumOfLevel(1, data)); // result 3
Console.WriteLine(GetSumOfLevel(2, data)); // result 4
Console.WriteLine(GetSumOfLevel(3, data)); // result 9
May be choose the first option?
i have these data:
class MyTableItem
{
public long id { get; set; }
public long listId { get; set; }
public long listFieldValue { get; set; }
public long parentId { get; set; }
}
and:
var myData = new MyTableItem[]
{
new MyTableItem { id = 1, listId = 1, listFieldValue = 100, parentId = 1 },
new MyTableItem { id = 2, listId = 2, listFieldValue = 130, parentId = 1 },
new MyTableItem { id = 3, listId = 3, listFieldValue = 170, parentId = 1 },
new MyTableItem { id = 4, listId = 4, listFieldValue = 170, parentId = 1 },
new MyTableItem { id = 5, listId = 1, listFieldValue = 100, parentId = 2 },
new MyTableItem { id = 6, listId = 2, listFieldValue = 130, parentId = 2 },
new MyTableItem { id = 7, listId = 3, listFieldValue = 170, parentId = 2 },
new MyTableItem { id = 8, listId = 4, listFieldValue = 270, parentId = 2 },
...(continue)
};
var myMatchConditions = new int?[][] //id, rangeTypeId(equal, more, less, between), from, to
{
new int?[] { 1, 1, 100, null },
new int?[] { 2, 2, 125, null },
new int?[] { 3, 3, null, 175 },
new int?[] { 4, 4, 130, 180 }
...(can continue more)
};
now i need to know which myData (groupBy parrentId) are matched by my conditions,
let me explain more:
I want to know which parrent Id has listFieldValue where:
1) (listId == 1)&&(listFieldValue == 100)
and
2) (listId == 2)&&(listFieldValue > 125)
and
3) (listId == 3)&&(listFieldValue < 175)
and
4) ((listId == 4)&&(listFieldValue > 130)&&(listFieldValue < 180))
it must return (1)parrentId.
There you go. Explanations are at the bottom:
IEnumurable<MyTableItem> temp = myData ;
for (int i = 0; i < myMatchConditions.GetLength(0); i++)
{
var conditionType = myMatchConditions[i,1];
if (conditionType == 1)
{
temp = temp.Where(_ => _listFieldValue == myMatchConditions[i,2]);
}
else
{
if (conditionType == 2 || conditionType == 4)
{
temp = temp.Where(_ => _listFieldValue > myMatchConditions[i,2]);
}
if (conditionType == 3 || conditionType == 4)
{
temp = temp.Where(_ => _listFieldValue < myMatchConditions[i,3]);
}
}
}
I'm using IEnumurable<MyTableItem> which means it's Linq and not Linq to entities. I chose that because your myData is not an EF table but a simple array.
I go through all the "rows" with a for, you can do that with a foreach, and I add the Where clauses to filter out more and more each time (The actual filtering will happen only when you use that temp list)
I add a condition based on the type in the second cell, and if the type is 4... I add both the 2 and 3 type rules... which makes a 4 type rule
Given this example data (in .NET classes where Po, Sku, Qty are properties):
PO, Sku, Qty
1,ABC,1
1,DEF,2
1,GHI,1
1,QWE,1
1,ASD,1
1,ZXC,5
1,ERT,1
2,QWE,1
2,ASD,11
2,ZXC,1
3,ERT,1
3,DFG,1
3,DFH,1
3,CVB,4
3,VBN,1
3,NMY,1
I need to transform it into a fixed column format, with a max of 5 SKUs per line (repeating the PO if needed for > 5):
PO, SkuA, QtyA, SkuB, QtyB, SkuC, QtyC, SkuD, QtyD, SkuE, QtyE
1, ABC, 1, DEF, 2, GHI, 1, QWE, 1, ASD, 1
1, ZXC, 5, ERT, 1, , , , , ,
2, QWE, 1, ASD, 11, ZXC, 1, , , ,
3, ERT, 1, DFG, 1, DFH, 1, CVB, 4, VBN, 1
3, NMY, 1, , , , , , , ,
Output can be CSV (which is what I'm outputting), or .NET classes - no matter there. Is there a simple way to do this in Linq by grouping by PO, then by counts of 5?
EDIT: I have no control of over the destination format. And for anyone interested, it's VendorNet and VendorBridge that require this nonsense.
Firstly, here's the query that will generate the correct hierarchy of objects. I'm using anonymous types but it's easy enough to change it to use your own proper classes.
var query = yourData
.GroupBy
(
x => x.PO
)
.SelectMany
(
x => x.Select
(
(y, i) => new { y.PO, y.Sku, y.Qty, Key = i / 5 }
)
)
.GroupBy
(
x => new { x.PO, x.Key }
);
Using LINQ to create the CSV from the query results is bit of a hack, but it gets the job done. (The "benefit" of using LINQ is that you could chain the original query and the CSV generation into a single, massive statement, should you wish.)
IEnumerable<string> csvLines = query
.Select
(
x => x.Aggregate
(
new { Count = 0, SB = new StringBuilder() },
(a, y) => new
{
Count = a.Count + 1,
SB = ((a.SB.Length == 0) ? a.SB.Append(y.PO) : a.SB)
.Append(", ").Append(y.Sku).Append(", ").Append(y.Qty)
},
a => a.SB.ToString() + string.Join(", , ", new string[6 - a.Count])
)
);
string csv = string.Join(Environment.NewLine, csvLines.ToArray());
In my opinion, creating the CSV without using LINQ makes the code much more readable:
StringBuilder sb = new StringBuilder();
foreach (var group in query)
{
int count = 0;
foreach (var item in group)
{
if (count++ == 0)
{
sb.Append(item.PO);
}
sb.Append(", ").Append(item.Sku).Append(", ").Append(item.Qty);
}
while (count++ < 5)
{
sb.Append(", , ");
}
sb.Append(Environment.NewLine);
}
string csv = sb.ToString();
Here you go. I didn't format the output the way you wanted. But this should give you an idea of how to pivot rows. Hope this helps :-)
public class MyClass
{
public int PO { get; set; }
public String SKU { get; set; }
public int Qty { get; set; }
public static IEnumerable<MyClass> GetList()
{
return new List<MyClass>()
{
new MyClass {PO = 1, SKU = "ABC", Qty = 1},
new MyClass {PO = 1, SKU = "DEF", Qty = 2},
new MyClass {PO = 1, SKU = "GHI", Qty = 1},
new MyClass {PO = 1, SKU = "QWE", Qty = 1},
new MyClass {PO = 1, SKU = "ASD", Qty = 1},
new MyClass {PO = 1, SKU = "ZXC", Qty = 5},
new MyClass {PO = 1, SKU = "ERT", Qty = 1},
new MyClass {PO = 2, SKU = "QWE", Qty = 1},
new MyClass {PO = 2, SKU = "ASD", Qty = 1},
new MyClass {PO = 2, SKU = "ZXC", Qty = 5},
};
}
}
EDIT: I've fixed the query based on Luke's comment
var lQuery =
MyClass.GetList()
.GroupBy(pArg => pArg.PO)
.Select(pArg => new
{
Test = pArg.Select((pArg1, pId) =>
new {ID = (pId / 5),
pArg1.PO, pArg1.SKU, pArg1.Qty})
.GroupBy(pArg1 => pArg1.ID)
.Select(pArg1 =>
pArg1.Aggregate(pArg.Key.ToString(),
(pSeed, pCur) =>
pSeed + pCur.SKU + ","))
});