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
Related
I am building a NET 5 API and am unable to extract and calculate something. I have a table StockTransaction which among other has property Quantity (I skipped some properties for brevity):
public class StockTransaction : BaseEntity
{
public int Id { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public bool Purchase { get; set; }
public int Resolved { get; set; }
}
Suppose I have 7 transactions, all of which are purchase:
List<StockTransaction> list = new List<StockTransaction>
{
new StockTransaction {Id = 1, Purchase = true, Quantity = 10, Resolved = 5, Price = 50},
new StockTransaction {Id = 2, Purchase = true, Quantity = 5, Resolved = 5, Price = 70},
new StockTransaction {Id = 3, Purchase = true, Quantity = 8, Resolved = 8, Price = 25},
new StockTransaction {Id = 4, Purchase = true, Quantity = 7, Resolved = 5, Price = 77},
new StockTransaction {Id = 5, Purchase = true, Quantity = 1, Resolved = 1, Price = 23},
new StockTransaction {Id = 6, Purchase = true, Quantity = 3, Resolved = 0, Price = 14},
new StockTransaction {Id = 7, Purchase = true, Quantity = 2, Resolved = 0, Price = 17},
};
And I would like to get the value of the last 7 quantities, which in this case gives 176 ((2 x 17) + (3 x 14) + (1 x 23) + (1 x 77)). (How) can this be done? Every help and hint is more then appreciated...
You can use
var last3=list.OrderByDescending(x=>x.CreatedDate).Take(3).Select(x=>x.Quantity * x.Price).Sum();
var requiredSum=last3+list.Where(x=>x.id==4).Select(x=>x.Price).FirstOrDefault();
Although you have tagged your question with LINQ, do it with a normal loop:
private static decimal SumOfLastTransaction(IEnumerable<StockTransaction> stockTransactions, int max)
{
decimal result = 0;
int sum = 0;
foreach(var stockTransaction in stockTransactions.OrderByDescending(x => x.Id))
{
if(sum + stockTransaction.Quantity <= max)
{
result += stockTransaction.Quantity * stockTransaction.Price;
sum += stockTransaction.Quantity;
}
else
{
result += (max - sum) * stockTransaction.Price;
return result;
}
}
return result;
}
You loop over the last items in your list. You sum the amount of transactions and check whether it is smaller than your maximum. As long as they are smaller, you can add the amount of transactions. If not, you check how much is missing until your maximum is reached and substract this from your quantity.
Online demo: https://dotnetfiddle.net/9oM4pj
How to translate the following SQL query into C# Linq:
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY StuffConditionId, StuffId ORDER BY StuffDayOfYear) AS RowNumber
FROM Stuff) rop
WHERE rop.RowNumber = 1;
Here is a partial dataset:
StuffId,StuffValue,StuffConditionId,StuffDayOfYear
2,9340,NULL,1
2,9340,NULL,2
2,9340,NULL,3
11,78,NULL,267
11,78,NULL,268
11,78,NULL,269
43,0,3,130
43,0,3,131
43,0,3,132
43,0,2,133
45,0,2,134
45,0,2,135
45,0,2,148
55,0,2,309
55,0,2,332
55,0,3,333
Answer Summary: The answer is to first build a in-memory list of stuffs, i.e. local list variable, then apply the LINQ query as shown in answer below.
Without some actual data I couldn't test this. But here's how this can be done, assuming stuff is your collection (aka table):
var firstInCollection = Stuff
.OrderBy(x => x.StuffDayOfYear)
.ToList() // Load in memory, then do groupby and select first due to EF Core
.GroupBy(x => new { condition = x.StuffConditionId, stuff = x.StuffId })
.Select(g => g.First());
Ok, I've tried this on a data table with a list of countries. Here are my results:
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY SUBSTRING(CountryCode, 1, 1) ORDER BY CountryCode) AS RowNumber
FROM Lookup.Country) rop
WHERE rop.RowNumber = 1;
and got the following results in SQL
Then I used the following Linq statement. This is using Linq2SQL in LinqPad connected to my Microsoft SQL Database:
Countries
.OrderBy(c => c.CountryName)
.GroupBy(c => c.CountryName[0])
.Select(g => g.First())
And got the following result:
Which correlates with the SQL results.
Here's the example with your example data
void Main()
{
var stuffs = new []
{
new Stuff { StuffId = 2, StuffValue = 9340, StuffConditionId = null, StuffDayOfYear = 1 },
new Stuff { StuffId = 2, StuffValue = 9340, StuffConditionId = null, StuffDayOfYear = 2 },
new Stuff { StuffId = 2, StuffValue = 9340, StuffConditionId = null, StuffDayOfYear = 3 },
new Stuff { StuffId = 11, StuffValue = 78, StuffConditionId = null, StuffDayOfYear = 267 },
new Stuff { StuffId = 11, StuffValue = 78, StuffConditionId = null, StuffDayOfYear = 268 },
new Stuff { StuffId = 11, StuffValue = 78, StuffConditionId = null, StuffDayOfYear = 269 },
new Stuff { StuffId = 43, StuffValue = 0, StuffConditionId = 3, StuffDayOfYear = 130 },
new Stuff { StuffId = 43, StuffValue = 0, StuffConditionId = 3, StuffDayOfYear = 131 },
new Stuff { StuffId = 43, StuffValue = 0, StuffConditionId = 3, StuffDayOfYear = 132 },
new Stuff { StuffId = 43, StuffValue = 0, StuffConditionId = 2, StuffDayOfYear = 133 },
new Stuff { StuffId = 45, StuffValue = 0, StuffConditionId = 2, StuffDayOfYear = 134 },
new Stuff { StuffId = 45, StuffValue = 0, StuffConditionId = 2, StuffDayOfYear = 135 },
new Stuff { StuffId = 45, StuffValue = 0, StuffConditionId = 2, StuffDayOfYear = 148 },
new Stuff { StuffId = 55, StuffValue = 0, StuffConditionId = 2, StuffDayOfYear = 309 },
new Stuff { StuffId = 55, StuffValue = 0, StuffConditionId = 2, StuffDayOfYear = 332 },
new Stuff { StuffId = 55, StuffValue = 0, StuffConditionId = 3, StuffDayOfYear = 333 }
};
var firstInCollection = stuffs
.OrderBy(x => x.StuffDayOfYear)
.GroupBy(x => new { condition = x.StuffConditionId, stuff = x.StuffId })
.Select(g => g.First())
.Dump();
}
class Stuff
{
public int StuffId { get; set; }
public int StuffValue { get; set; }
public int? StuffConditionId { get; set; }
public int StuffDayOfYear { get; set; }
}
This results in the following:
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
}
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
Say i have a class that contains these items publicly accessible via properties:
class MyClass
{
int switch1; //0 or 1
int switch2; //0 or 1
int switch3; //0 or 1
}
This class represents switch states, and each time a switch state changes, i would like to add it to my transition list
I have a large sorted list that contains instances of this class and would like to use a query to capture only the entries in my list where the switch state for any switch changes.
Is this possible using a linq query?
try this:
Assuming your class looks like:
public class State
{
public int Id { get; set; }
public int Switch1 { get; set; }
public int Switch2 { get; set; }
public int Switch3 { get; set; }
public override bool Equals(object obj)
{
var other = obj as State;
if (other != null)
{
return Switch1 == other.Switch1 &&
Switch2 == other.Switch2 &&
Switch3 == other.Switch3;
}
return false;
}
}
I just added an Equals() to compare flags and my Id field is purely to demonstrate which items changed.
We can then craft a LINQ query like:
State previous = null;
var transitions = list.Where(s =>
{
bool result = !s.Equals(previous);
previous = s;
return result;
})
.ToList();
Not elegant, but it works, if you had this data set:
var list = new List<State>
{
new State { Id = 0, Switch1 = 0, Switch2 = 0, Switch3 = 0 },
new State { Id = 1, Switch1 = 0, Switch2 = 0, Switch3 = 0 },
new State { Id = 2, Switch1 = 1, Switch2 = 0, Switch3 = 0 },
new State { Id = 3, Switch1 = 0, Switch2 = 1, Switch3 = 0 },
new State { Id = 4, Switch1 = 0, Switch2 = 1, Switch3 = 0 },
new State { Id = 5, Switch1 = 0, Switch2 = 1, Switch3 = 0 },
new State { Id = 6, Switch1 = 1, Switch2 = 1, Switch3 = 0 },
new State { Id = 7, Switch1 = 0, Switch2 = 0, Switch3 = 1 },
new State { Id = 8, Switch1 = 0, Switch2 = 0, Switch3 = 1 },
new State { Id = 9, Switch1 = 0, Switch2 = 0, Switch3 = 0 },
};
And ran the query, the list would contain your state transitions at items: 0, 2, 3, 6, 7, 9
I would do as follow:
class MyClass
{
int ID; //needs for recognize the message
int switch1; //0 or 1
int switch2; //0 or 1
int switch3; //0 or 1
public int Pattern
{
get { return switch1 + switch2 << 1 + switch3 << 2; }
}
}
Then it must be declared a dictionary with the previous-state messages:
Dictionary<int, int> _prevStates;
each cell has for key the ID, and for value the "Pattern" of the message.
At this point, let's suppose that the new incoming message stream is a list of MyClass:
IEnumerable<MyClass> incoming = ...
var changed = from msg in incoming
where _prevStates.ContainsKey(msg.ID) //what to do?
where _prevStates[msg.ID].Pattern != msg.Pattern
select msg;
Finally, you must update the dictionary with the changed patterns.
Cheers