Creating key-value pairs from SQL result that have duplicate data - c#

My problem at the moment is I have results coming back from a SQL query that returns a result like this:
125 Month 10.00 Wholesale
125 Year 20.00 Wholesale
126 Month 20.00 Wholesale
126 Year 30.00 Wholesale
127 Month 40.00 Wholesale
127 Year 50.00 Wholesale
where integer column is the ID of the column.
when the data gets returned to the C# calling code, it is placed into an object followering this structure:
PuctName;
}
I am just having issues with how to create the terms without causing an endless amount of loops.

you can use a Dictionary that way you will have a key value pair. the key is ProductID and the value the list of Terms.
var dictionary = new Dictionary<int, List<Terms>>();
foreach (ProductTermAndPricingDataItem item in productInformationItems)
{
if(dictionary.ContainsKey(item.ProductID))
{
dictionary[item.ProductID].Add(new Terms { Term = item.BillingPeriodName, Price = item.PriceAmount});
}
else
{
dictionary.Add(item.ProductID, new List<Terms>() { new Terms() {Term = item.BillingPeriodName, Price = item.PriceAmount } });
}
}

You can use Linq and GroupBy:
List<ProductPricingGetDataItem> grouped = productInformationItems.GroupBy(
p => p.ProductID,
(key, g) => new ProductPricingGetDataItem() { ProductID = key, Terms = g.Select(x => new Terms(x.BillingPeriodName, x.PriceAmount)).ToList() }).ToList();
In order for that code to work, you need to add a constructor to Terms :
public Terms(string term, decimal price)
{
Term = term;
Price = price;
}
Fiddle with working example : https://dotnetfiddle.net/EE2BpP

For LINQ lovers:
//Your initial data list
var productInformationItems = new List<ProductTermAndPricingDataItem>();
var productPricingGetDataItems = productInformationItems.ToLookup(item => item.ProductID)
.Select(grouping => new ProductPricingGetDataItem
{
ProductID = grouping.Key,
Terms = grouping.Select(item => new Terms
{
Price = item.PriceAmount,
Term = item.BillingPeriodName
}).ToList()
}).ToList();
In your exact case the result is :
Feel free to ask if something is not clear.

Related

Linq to select highest ID where the ID is alphanumeric

I have a list of ID where the ID's start with MB (for members) or NM (for non members).
The ID would be like MB-101 or NM-108 etc...
I am trying to select the Highest ID starting with MB and then Add 1 and then save back to DB. Saving is easy but I am not sure how to query the highest Member or Nonmember ID and add one to it. Any help is much appreciated
You can do something like this:
List<string> list = new List<string>() { "MB-101", "MB-102", "MB-103", "MB-104"};
var ids = list.Select(x =>Convert.ToInt32(x.Replace("MB-", "")));//convert all the number parts to integer
list[list.FindIndex(x => x == "MB-" + ids.Max().ToString())] = "MB-" + (ids.Max() + 1);//set the max number after adding one.
You can do the same with your Nonmember ID. It is tested code, it successfully addresses your problem.
Hope it helps.
You can get the max id by splitting your list like below:
var ids = yourList.Where(x => x.ID.StartsWith("MB-")).Select(x => new { ID = x.ID.Split('-')[1] }).ToList();
var maxIdValue = ids.Select(x => int.Parse(x.ID)).ToList().Max();
If you want max id from both starting with MB- and NB- than you can remove above where condition. By this it will fetch max id from both MB- and NB-. Following will be query than:
var ids = yourList.Select(x => new { ID = x.ID.Split('-')[1] }).ToList();
var maxIdValue = ids.Select(x => int.Parse(x.ID)).ToList().Max();
you can try like this
List<string> lststr = new List<string>() { "MB-101", "MB-103", "MB-102", "NM-108" };
var result = "MB-"+ lststr
.Where(x=>x.Contains("MB"))
.Select(x => Regex.Replace(x, #"[^\d]", ""))
.OrderByDescending(x=>x)
.FirstOrDefault();
it will return MB-103 because it will first check if the string contains MB then it will replace everything with "" other than digit and OrderByDescending it will order by Descending so that the highest value will be on top and at last FirstOrDefault will get the fist value
You have to do OrderByDescending your list by replacing MB or NM with empty and get FirstOrDefault from ordered list. Please check below example.
CODE:
List<string> list = new List<string>() { "MB-101", "MB-102", "MB-109", "MB-105", "NM-110"};
var maxMember = list.OrderByDescending(m=>Convert.ToInt16(m.Replace("MB-","").Replace("NM-",""))).ToList().FirstOrDefault();
Console.WriteLine(maxMember.ToString());

How to order a collection by a property first and then by another property regardless of tie on first property?

What I am trying to achieve is order the OrderSummary by highest Amount first and then display all other Order(s) in the collection for the given account one after another regardless of the Amount. Expected outcome is in the code snippet..
public class OrderSummary
{
public string FirstName { get; set; }
public decimal Amount { get; set; }
}
public class Worker
{
public List<OrderSummary> Orders { get; set; }
public Worker()
{
Orders = new List<OrderSummary>()
{
new OrderSummary() {FirstName = "James", Amount = 10.00m},
new OrderSummary() {FirstName = "Thomas", Amount = 11.00m},
new OrderSummary() {FirstName = "Leon", Amount = 13.00m},
new OrderSummary() {FirstName = "Lori", Amount = 14.00m},
new OrderSummary() {FirstName = "Thomas", Amount = 16.00m},
new OrderSummary() {FirstName = "Thomas", Amount = 6.00m},
new OrderSummary() {FirstName = "James", Amount = 19.00m}
};
}
//sorted by highest amount first
//then place firstname together regardless of the amount
//Expected Outcome
/*
James 19 -- highest amount followed by all other orders for James regardless of the amount.
* james 10
* thomas 16
* thomas 11
* thomas 6
* lori 14
* leon 13
*/
}
My approach was to get all elements that occurs more than once, then locate at which index the elements are except for the first one, remove it from the index, and add it to first element's index + 1.. Is there a better way to accomplish this?
GroupBy creates groups in the order that they appear in the source collection, so you can first order by amount, then group by name, then "flatten" the groups:
var results = Orders.OrderByDescending(o => o.Amount)
.GroupBy(o => o.FistName) // the groupings will be in order of the largest amount
.SelectMany(g => g); // flatten the groups
Try this:
var list = Orders.OrderByDescending(y => y.Amount).GroupBy(x => x.FirstName).Select(x=>x.Key).ToList();
This is ordering the records descending, then it is grouping them by FirstName, so you will have the one with the highest Amount first, then the others.
var maxAmount = Orders.Max(o => o.Amount);
var maxPos = Orders.IndexOf(maxAmount);
var maxOrder = Orders[maxPos];
Orders.RemoveAt(maxPos);
Orders.Insert(0, maxOrder);
EDIT
I didn't get that "one after another" would mean "grouped by name"...
Sorry, I have an example for you using .Net fiddle
Here it is
Answer To Question
Let me know if this helps
My approach was to get all elements that occurs more than once, then locate at which index the elements are except for the first one, remove it from the index, and add it to first element's index + 1
Here is the LINQ way of doing exactly what are you describing:
var query = from o in Orders
group o by o.FirstName into g
let top = g.Aggregate((a, b) => b.Amount > a.Amount ? b : a)
from o in Enumerable.Repeat(top, 1).Concat(g.Where(o => o != top))
select o;
You start by grouping the Orders by FirstName, then for each group you locate the element with the maximum Amount using Aggregate, put it first in a group and append the others. Finally flatten the result.

LINQ to Entities - Simple Way to split resultset into multiple objects

I have a table, containing weekly sales data from multiple years for a few hundred products.
Simplified, I have 3 columns: ProductID, Quantity, [and Date (week/year), not relevant for the question]
In order to process the data, i want to fetch everything using LINQ. In the next step I would like create a List of Objects for the sales data, where an Object consists of the ProductId and an array of the corresponding sales data.
EDIT: directly after, I will process all the retrieved data product-by-product in my program by passing the sales as an array to a statistics software (R with R dot NET) in order to get predictions.
Is there a simple (built in) way to accomplish this?
If not, in order to process the sales product by product,
should I just create the mentioned List using a loop?
Or should I, in terms of performance, avoid that all together and:
Fetch the sales data product-by-product from the database as I need it?
Or should I make one big List (with query.toList()) from the resultset and get my sales data product-by-product from there?
erm, something like
var groupedByProductId = query.GroupBy(p => p.ProductId).Select(g => new
{
ProdcutId = g.Key,
Quantity = g.Sum(p => p.Quantity)
});
or perhaps, if you don't want to sum and, instread need the quantities as an array of int ordered by Date.
var groupedByProductId = query.GroupBy(p => p.ProductId).Select(g => new
{
ProdcutId = g.Key,
Quantities = g.OrderBy(p => p.Date).Select(p => p.Quantity).ToArray()
});
or maybe you need to pass the data around and an anonymous type is inappropriate., you could make an IDictionary<int, int[]>.
var salesData = query.GroupBy(p => p.ProductId).ToDictionary(
g => g.Key,
g => g.OrderBy(p => p.Date).Select(p => p.Quantity).ToArray());
so later,
int productId = ...
int[] orderedQuantities = salesData[productId];
would be valid code (less the ellipsis.)
You may create a Product class with id and list of int data. Something as below:
Public class Product{
public List<int> list = new List<int>();
public int Id;
Public Product(int id,params int[] list){
Id = id;
for (int i = 0; i < list.Length; i++)
{
list.Add(list[i]);
}
}
}
Then use:
query.where(x=>new Product(x.ProductId,x.datum1,x.datum2,x.datum3));

Speed of linq query grouping and intersect in particular

Say 3 lists exist with over 500,000 records and we need to perform a set of operations (subsets shown below):
1) Check for repeating ids in list one and two and retrieve distinct ids while Summing up "ValuesA" for duplicate ids and put results in a list. Lets call this list list12.
2) compare all the values with matching ids between list 3 list12 and print results say to console.
3) ensure optimal performance.
This what i have so far:
var list1 = new List<abc>()
{
new abc() { Id = 0, ValueA = 50},
new abc() { Id = 1, ValueA = 40},
new abc() { Id = 1, ValueA = 70}
};
var list2 = new List<abc>()
{
new abc() { Id = 0, ValueA = 40},
new abc() { Id = 1, ValueA = 60},
new abc() { Id = 3, ValueA = 20},
};
var list3 = new List<abc>()
{
new abc() { Id = 0, ValueA = 50},
new abc() { Id = 1, ValueA = 40},
new abc() { Id = 4, ValueA = 70},
};
1) with the help of the solution from here [link][1] I was able to resolve part 1.
var list12 = list2.GroupBy(i => i.Id)
.Select(g => new
{
Id = g.Key,
NewValueA = g.Sum(j => j.ValueA),
});
2)I cant seem to properly get the complete result set from this part. I can get the matching account numbers, maybe someone knows of a faster way other than hashsets, but I also need the ValueA from each list along with the matching account numbers.
foreach (var values in list3.ToHashSet().Select(i => i.ID).Intersect(list12.ToHashSet().Select(j => j.UniqueAccount)))
{
Console.WriteLine(values) //prints matching account number
//?? how do I get ValueA with from both lists with this in the quickest way possible
}
3) my only attempt at improving performance from reading online is to use hashsets as I seen in the attempt above but I may be doing this incorrectly and someone may have a better solution
I don't think that any conversion to HashSet, however efficient, will increase performance. The reason is that the lists must be enumerated to create the HashSets and then the HashSets must be enumerated to get to the results.
If you put everything in one LINQ statement the number of enumerations will be minimized. And by calculating the sums at the end the number of calculations is reduced to the absolute minimum:
list1.Concat(list2)
.Join(list3, x => x.Id, l3 => l3.Id, (l12,l3) => l12)
.GroupBy (x => x.Id)
.Select(g => new
{
Id = g.Key,
NewValueA = g.Sum(j => j.ValueA),
})
With your data this shows:
Id NewValueA
0 90
1 170
I don't know if I understood all requirements well, but this should give you the general idea.
If you want to get access to both elements you probably want a join. A join is a very general construct that can be used to construct all other set operations.

C# Merging 2 dictionaries and adding values between them

I am learning C# and needed to merge two dictionaries so I could add values found in both.
The first dictionary is payePlusNssf that holds a value for each key (key represents employee ID). So far I have employees 1,2,3,4 and 5
Dictionary<int, decimal> payePlusNssf = new Dictionary<int, decimal>();
paye.ToList().ForEach(x =>
{
var deductNSSF = x.Value + nssfAmount;
payePlusNssf.Add(x.Key, deductNSSF);
});
The 2nd dictionary is nhifRatesDictionary that holds rates to be added to each value per employee in the first dictionary.
Dictionary<int, IEnumerable<NHIFRates>> nhifRatesDictionary =
new Dictionary<int, IEnumerable<NHIFRates>>();
basicPayDictionary.ToList().ForEach(x =>
{
List<NHIFRates> nhifValueList = new List<NHIFRates>();
// Using Employee basic pay
decimal basicPay = x.Value;
bool foundflag = false;
foreach (var item in nhifBracketList)
{
if (basicPay >= item.Min && basicPay <= item.Max)
{
nhifValueList.Add(new NHIFRates { Rate = item.Rate });
foundflag = true;
break;
}
}
if (!foundflag)
{
nhifValueList.Add(new NHIFRates { Rate = 0m });
}
nhifRatesDictionary.Add(x.Key, nhifValueList);
});
struct NHIFRates
{
public decimal Rate { get; set; }
}
In summary I need this after merging and adding:
Dict 1 Dict 2 Result Dict 3
key value key rate key value
1 500 1 80 1 580
2 1000 2 100 2 1100
3 2000 3 220 3 2220
4 800 4 300 4 1100
5 1000 5 100 5 1100
How do I achieve this? I have looked at past similar problems on this site but have not been very helpful to me.
Not tested but try:
payePlusNssf.ToDictionary(
v => v.Key,
v => v.Value + nhifRatesDictionary[v.Key].Sum(nhifr => nhifr.Rate)
);
This assumes that since the value of nhifRatesDictionary is IEnumreable you want to sum over all the values in the enumerable. This should also work if the IEnumerable is empty. If you know that there is exactly one value for each key then you can use:
payePlusNssf.ToDictionary(
v => v.Key,
v => v.Value + nhifRatesDictionary[v.Key].Single(nhifr => nhifr.Rate)
);
What about a simple for cycle?
Dictionary<int,decimal> d3 = new Dictionary<int,decimal>();
for (int i = 1,i<=payePlusNssf.Count,i++)
{
d3.Add (i,payePlusNssf[i]+((nhifRatesDictionary[i])[0]).Rate);
}
If the ID numbers are not guranteed to be this simple you can use
foreach (var x in payePlusNssf)
{
d3.Add(x.Key,x.Value+ ((nhifRatesDictionary[x.Key])[0]).Rate);
}
Or do it completely differently, do not keep three separate dictionaries that are guaranteed to have the same keys and create an employee class like
class Employee
{
public decimal payePlusNssf;
public decimal nhifRate;
public decimal Sum
{
get { return payePlusNssf + nhifRate ;}
}
}
and have one Dictionary with everything - saves you problems with keeping the dictionaries all updated.

Categories

Resources