Filter C# Entity List - c#

Hi to the helpfull community!
Guys I have a problem with a C# Entity/SQL Server 2014 project that gives me hard times
I have a procedure with this result set
OnDate RoomType Available RateCode Price
2016-12-12 DBL 2 BAR 200
2016-12-12 TRP 4 BAR 300
2016-12-12 DBL 2 NEW 250
2016-12-12 TRP 4 NEW 350
In my C# program I have a List that is fed from that procedure through the Entyity Framework
In order not to create another procedure and another LIST, is there a way, depending on my needs, to transform my List result within C# as follows?
OnDate RoomType Available RateCode Price
2016-12-12 DBL 2 0
2016-12-12 TRP 4 0
So it is basically the same result but DISTINCT with the RateCode empty and the Price = 0
I need both results sets for my processing. I do for loops and populate XMLs so when I need only RoomType-Availability if I am stuck with the first result I would get the same Room Types as many tmes as RateCodes exist which is not acceptable
for (int Row = 0; Row < myList.Count(); Row++)
{
blah
}
Thanx in advance!

This will return your desired list. You are grouping your collection by RoomType and Available and after that Select the data which you need.
var result = myList.GroupBy(x => new { x.RoomType, x.Available })
.Select(g => new Hotel {
OnDate = g.First().OnDate,
RoomType = g.Key.RoomType,
Available = g.Key.Available,
RateCode = "",
Price = 0 })
.ToList();
Here full code example: dotNetFiddle

Related

How to calculate a cummulative sum in LINQ?

I developed my program with C# with Entity Framework and using LINQ for the query. And I have a problem while converting my SQL query to LINQ.
I don't know what to do, please.
Here is my SQL query in SQL Server:
SELECT bs.idBalancesortie, bs.datesortie, bs.c_num_debut,bs.c_num_fin,bs.nombredetickets
, n.quotite, bs.montant
, SUM(bs.montant) OVER (ORDER BY bs.idNature ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as Cumul
, bs.idNature
FROM BalanceSortie bs
LEFT JOIN Natures n on bs.idNature = n.idNature
order BY bs.idNature
it calculates the cumulative sum of the "montant" but I want this cumulative sum to be done by "idNature"
For example
id Category montant cumul
1 C 49 49
2 A 4 4
3 A 16 20
4
Updated for cumul reset to zero for each idNature
I have not tested this solution, but the key to getting a running sum is to create the "cumul" variable and use a multi-statement lambda. This will require early execution (.ToList() or AsEnumerable()) after the Join.
I added a second variable that holds the "current idNature. Assumed it to be an int and that it cannot be -1.
decimal cumul = 0; // define a variable
int idNature = -1;
var target = BalanceSorties
.Join(idNature, bs => bs.idNature, n => n.idNature, (bs, n) => new { bs, n })
.OrderBy(q => q.n.idNature)
.AsEnumerable()
.Select(q =>
{
if (idNature != q.n.idNature)
{
cumul =0;
idNature = q.n.IdNature;
}
cumul += q.bs.montant;
return new
{
q.bs.idBalancesortie,
q.bs.datesortie,
q.bs.c_num_fin,
q.bs.nombredetickets,
q.n.quotité,
Cumul = cumul,
q.bs.idNature
};
})
.ToList();

Buy X Pay for Y Algorithm

I have to write "Buy X Pay for Y" algorithm.
Request that comes to my endpoint is list of Articles
public class Article
{
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
}
payFor variable comes from db and is defined by package discount id
Here's my algorithm that I wrote so far
if (purchasedQuantity >= minPurchaseQuantity)
{
var c = 0;
foreach (var article in articlesForPackage.OrderByDescending(a => a.UnitPrice))
{
for (var i = 1; i <= article.Quantity; i++)
{
c++;
if (c > payFor)
{
c = 0;
result.Add(new Discount
{
Value = article.UnitPrice
});
}
}
}
}
Unfortunately, this algorithm does not work in some cases.
When the package discount is defined buy 3 and pay for 2 it works, but if buy 3 pay for one doesn't work.
Could somebody help me?
This is how the algorithm should work:
We have 3 articles
1 Art1 - 20$
2 Art2 - 30$
3 Art3 - 40$
If minPurchaseQuantity is 3 and payFor is 2 it means that cost of Art1 should be added to result list (because it is the cheapest one)
If minPurchaseQuantity is 3 and payFor is 1 it means that cost of Art2 and Art1 should be added to result list (Now only the Art2 is adding)
Well, the main issue is, that you reset c as soon as it gets larger than payFor. This works as long as minPurchaseQuantity-payFor=1, but in other cases it won't.
While it's not as easy as the solution I presented in my first answer, I think the actual algorithm can be implemented more concisely. The following code first batches the items in groups eligible for discount. For each of the batches it then skips as many as payFor items and calculates the discount from the rest
// first batch the items in batches eligible for discount (e.g. batches of three in take 3, pay for x)
var batchedItems = BatchItemsEligibleForDiscount(items, minPurchaseQuantity);
var discounts = batchedItems.Select(batch => batch.Skip(payFor))
.SelectMany(batch => batch) // flatten nested IEnumerable to an IEnumerable<Artible>
.Select(article => new Discount() { Value = article.UnitPrice });
The BatchItemsEligibleForDiscount gets the batches that are eligible for discount (i.e. have 3 items each if it's "take 3, pay for X". Articles with a Quantity>1 are "exploded", i.e. if the quantity is 3, 3 distinct objects are created.
IEnumerable<IEnumerable<Article>> BatchItemsEligibleForDiscount(items, minPurchaseQuantity)
{
return items.OrderByDescending(article => article.UnitPrice)
.Select(article => Enumerable.Range(1, article.Quantity).Select(n => new Article() { Quantity = 1, UnitPrice = article.UnitPrice })) // "explode" articles
.SelectMany(item => item) // flatten to an IEnumerable<Article>
.Select((article, index) => new { article, index })
.GroupBy(x => x.index / minPurchaseQuantity)
.Where(group => group.Count() == minPurchaseQuantity) // only take batches elegible for discount
.Select(group => group.Select(x => x.article));
}
See this fiddle for a demonstration.
OLD ANSWER
Calculating the discount is way easier. You can calculate the number of bundles elegible for discount (if its take 3, pay for 2 and 8 items, you have two whole bundles of 3 items each). By calculating the difference between the items to take and the items to pay and multiplying it with the number of bundles and the price per item, you can calculate the discount
var numberOfDiscountableBundles = article.Quantity / amountOfItemsElegibleForDiscount;
var discount = numberOfDiscountableBundles * (amountOfItemsElegibleForDiscount - payFor) * article.UnitPrice;
Example: Take 3, pay for 1 with 8 items:
numberOfDiscountableBundles = 8 / 3 = 2 (integer division!)
discount = 2 * (3 - 1) * p = 2 * 2 * p = 4 * p
It's two discounted bundles of three items each (six items). Four of those items are not payed for (only one per bundle), hence the total price is discounted by four times the price of a unit.
You could encapsule this in a method
Discount CalculateDiscountForArticle(Article article, int amountOfItemsElegibleForDiscount, int payFor)
{
var numberOfDiscountableBundles = article.Quantity / amountOfItemsElegibleForDiscount;
var discount = numberOfDiscountableBundles * (amountOfItemsElegibleForDiscount - payFor) * article.UnitPrice;
return new Discount
{
Value = discount
};
}
And your original function gets as easy as
var discounts = articlesForPackage.OrderByDescending(a => a.UnitPrice)
.Select(a => CalculateDiscountForArticle(a, amountOfItemsElegibleForDiscount, payFor));
EDIT TO OLD ANSWER
If the discount is granted only once per customer and article, the calculation is a bit different
double discount = 0;
if(article.Quantity >= amountOfItemsElegibleForDiscount)
{
var discount = (amountOfItemsElegibleForDiscount - payFor) * article.UnitPrice;
}

How to use linq to calculate average from grouped data where value > 0 using let

I need to calculate an average of pretty complicated structure of enuberable data.
I have var table1Sum that is joined from two datatables. Another var table2Sum is also joined from two datatables. These two tables are joined into third table.
I need to get average of field3 grouped by field1 and field2 but the average calculation should not take into count when field3 == 0.0.
I tried
var table3 = from d in table1sum.Concat(table2sum)
group d by new { d.field1, d.field2 } into dg
let field3Average = dg.Where(g => g.field3 > 0.0).Average()
select new
{
.....
};
but this is not correct syntax for calculating average. I'm using let because I need calculated average later on in the select new part of my linq query.
(The normal average of all values goes well with let averageWithZeros = dg.Average(g => g.field3).)
Could somebody help me with correct syntax?
you need to average field 3 after the filter
let field3Average = dg.Where(g => g.field3 > 0.0).Average(g => g.field3)

Group by with a foreign key on single LINQ

I have 2 related tables and they are
Schedule
SlotId
Description
Amount
and Slot
Id
Name
Example data:
Schedule
slotid description amount
--------------------------------
1 Morning charge 300
1 Late fee 300
1 Earlier bonus 200
1 Half day 150
2 Morning charge 300
2 Late fee 300
2 Earlier bonus 200
2 Half day 150
3 Morning charge 300
3 Late fee 300
3 Earlier bonus 200
3 Half day 150
Final result wanted as a SlotSchedules list, like this:
SlotId
SlotName
List<Schedules>
I need:
fetched list of Schedules
and then find distinct slots in it
and then iterate through each slot and build the model i needed as below
This is what I tried with LINQ:
List<Schedules> schedulesAll = (from n in dbContext.Schedules
select n).ToList();
var slotsdistinct = schedulesAll.Select(x => x.SlotId).Distinct().ToList();
foreach (var slot in slotsdistinct)
{
var scheduledforslot = schedulesAll.Where(x => x.SlotId == slot).Select(x => x).ToList();
foreach (Schedules _schedule in scheduledforslot)
{
//ListModel.Add(new DetailsModel { Name = _schedule.Description, Amount = (_schedule.Amount });
}
}
Any way to make it in single LINQ query?
Try this:
var slotsSchedules = dbContext.Slots.ToList().Select(slot => new {
SlotID = slot.Id,
Name = slot.Name,
SlotSchedules = dbContext.Schedules.ToList()
.Where(schedule = > schedule.SlotId == slot.Id).ToList()});

Counting number of items in ObservableCollection where it equals 1 - C#

I made an SQL query and filled the data to an ObservableCollection. The database contains many columns so I want to count how many instances where a specific column = 1, then return that number to an int.
The query:
var test = from x in m_dcSQL_Connection.Testheaders
where dtStartTime <= x.StartTime && dtEndtime >= x.StartTime
select new {
x.N,
x.StartTime,
x.TestTime,
x.TestStatus,
x.Operator,
x.Login,
x.DUT_id,
x.Tester_id,
x.PrintID
};
Then I add the data pulled from the database to an Observable Collection via:
lstTestData.Add(new clsTestNrData(item.N.ToString(),
item.StartTime.ToString(),
item.TestTime.ToString()
etc.....
I want to count how many times TestStatus = 1.
I have read about the .Count property but I do not fully understand how it works on ObservableCollections.
Any help?
The standard ObservableCollection<T>.Count property will give you the number of items in the collection.
What you are looking for is this:
testStatusOneItemCount = lstTestData.Where(item => item.TestStatus == 1).Count()
...which uses IEnumerable<T>.Count() method which is part of LINQ.
To elaborate a bit, Count will simply count the objects in your collection.
I suggest having a quick look at linq 101. Very good examples.
Here's an example:
// Assuming you have :
var list = new List<int>{1,2,3,4,5,6 };
var items_in_list = list.Count(); // = 6;
Using linq's Where, you're basically filtering out items, creating a new list. So, the following will give you the count of all the numbers which are pair:
var pair = list.Where(item => item%2 ==0);
var pair_count = pair.Count; // = 3
You can combine this without the temp variables:
var total = Enumerable.Range(1,6).Where(x => x % 2 ==0).Count(); // total = 6;
Or you can then select something else:
var squares_of_pairs = Enumerable.Range(1,6)
.Where(x => x % 2 ==0).
.Select( pair => pair*pair);
// squares_of_pairs = {4,16, 36}. You can count them, but still get 3 :)

Categories

Resources