Unable to get a distinct list that contains summed values - c#

I have posted this earlier but the objective of what I am trying to achieve seems to have lost hence re-posting it to get explain myself better.
I have a collection that has duplicate productnames with different values. My aim is to get a list that would sum these productnames so that the list contains single record of these duplicates.
For e.g
If the list contains
Product A 100
Product A 200
The result object should contain
Product A 300
So as you can see in my code below, I am passing IEnumerable allocationsGrouped to the method. I am grouping by productname and summing the Emv fields and then looping it so that I created a new list of the type List and pass it to the caller method. The problem what I seeing here is on the following line of code Items = group. Items now contains original list without the sum. Hence the inner foreach loop runs more than ones because there are duplicates which defeats my purpose. I finally need to return result object that has non duplicate values which are summed based on the above criteria. Could you please tell me where I am going wrong.
private static List<FirmWideAllocationsViewModel> CreateHierarchy(string manStratName, IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationsGrouped, List<FirmWideAllocationsViewModel> result)
{
var a = allocationsGrouped
.Where(product => !string.IsNullOrEmpty(product.PRODUCT_NAME))
.GroupBy(product => product.PRODUCT_NAME)
.Select(group => new
{
ProductName = group.Key, // this is the value you grouped on - the ProductName
EmvSum = group.Sum(x => x.EMV),
Items = group
});
var b = a;
var item = new FirmWideAllocationsViewModel();
item.Hierarchy = new List<string>();
item.Hierarchy.Add(manStratName);
result.Add(item);
foreach (var ac in b)
{
var productName = ac.ProductName;
var emvSum = ac.EmvSum;
foreach (var elem in ac.Items)
{
var item2 = new FirmWideAllocationsViewModel();
item2.Hierarchy = new List<string>();
item2.Hierarchy.Add(manStratName);
item2.Hierarchy.Add(elem.PRODUCT_NAME);
item2.FirmID = elem.FIRM_ID;
item2.FirmName = elem.FIRM_NAME;
item2.ManagerStrategyID = elem.MANAGER_STRATEGY_ID;
item2.ManagerStrategyName = elem.MANAGER_STRATEGY_NAME;
item2.ManagerAccountClassID = elem.MANAGER_ACCOUNTING_CLASS_ID;
item2.ManagerAccountingClassName = elem.MANAGER_ACCOUNTING_CLASS_NAME;
item2.ManagerFundID = elem.MANAGER_FUND_ID;
item2.ManagerFundName = elem.MANAGER_FUND_NAME;
item2.Nav = elem.NAV;
item2.EvalDate = elem.EVAL_DATE.HasValue ? elem.EVAL_DATE.Value.ToString("MMM dd, yyyy") : string.Empty;
item2.ProductID = elem.PRODUCT_ID;
item2.ProductName = elem.PRODUCT_NAME;
item2.UsdEmv = Math.Round((decimal)elem.UsdEmv);
item2.GroupPercent = elem.GroupPercent;
item2.WeightWithEq = elem.WEIGHT_WITH_EQ;
result.Add(item2);
}
}
return result;
}

change it to:
var result = allocationsGrouped
.Where(product => !string.IsNullOrEmpty(product.PRODUCT_NAME))
.GroupBy(product => product.PRODUCT_NAME)
.Select(group => {
var product = group.First();
return new FirmWideAllocationsViewModel()
{
Hierarchy = new List<string>() { manStratName, product.PRODUCT_NAME },
FirmID = product.FIRM_ID,
FirmName = product.Item.FIRM_NAME,
ManagerStrategyID = product.MANAGER_STRATEGY_ID,
ManagerStrategyName = product.MANAGER_STRATEGY_NAME,
ManagerAccountClassID = product.MANAGER_ACCOUNTING_CLASS_ID,
ManagerAccountingClassName = product.MANAGER_ACCOUNTING_CLASS_NAME,
ManagerFundID = product.MANAGER_FUND_ID,
ManagerFundName = product.MANAGER_FUND_NAME,
Nav = product.NAV,
EvalDate = product.EVAL_DATE.HasValue ? product.EVAL_DATE.Value.ToString("MMM dd, yyyy") : string.Empty,
ProductID = product.PRODUCT_ID,
ProductName = product.PRODUCT_NAME,
UsdEmv = Math.Round((decimal)product.UsdEmv),
GroupPercent = product.GroupPercent,
WeightWithEq = product.WEIGHT_WITH_EQ,
//assign aggregate Sum here
EmvSum = group.Sum(x => x.EMV),
};
});

Related

display two table data at a time in linq

I want to to display two tables information at a time.
List<int> order_track = db.Order_Trackings.Where(e => e.UID == id).Select(q => q.ID).ToList();
if (order_track == null)
{
var rate = db.Ratings.OrderByDescending(e => e.Rate).Take(5);
}
List<int> fidList = db.OrderFoods.Where(q => order_track.Contains(q.OID)).Select(q => q.FID).ToList();
var qs = (from x in fidList
group x by x into g
let count = g.Count()
orderby count descending
select new { KEY = g.Key });
if (order_track.Count == 2)
{
var one = qs;
List<int> idList = new List<int>();
foreach (var val in one)
{
idList.Add(val.KEY);
}
var food = db.Foods.Where(q => idList.Contains(q.ID));
var rate = db.Ratings.OrderByDescending(e => e.Rate).FirstorDefault();
return Request.CreateResponse(HttpStatusCode.OK, rate);
I want to do something like this I hope you will understand what i am trying to achieve Thanks in advance.
var food = db.Foods.Where(q => idList.Contains(q.ID)&&db.Ratings.OrderByDescending(e => e.Rate).FirstorDefault());
return Request.CreateResponse(HttpStatusCode.OK, rate);
If you want to combine the two results into one variable, then the easiest way to do so is by creating an anonymous object, like this:
var result = new
{
food = db.Foods.Where(q => idList.Contains(q.ID)),
rate = db.Ratings.OrderByDescending(e => e.Rate).FirstorDefault()
};
return Request.CreateResponse(HttpStatusCode.OK, result);
You could also create a class with two properties and then create an instance of that class, but if this is the only place where you would use that class then I wouldn't bother doing that.

Filter and add values using C# using lambda expression

New to C# and appreciate any help. The issue is that I need to filter the results of my api call against an array (using an "allowedA" and "allowedB" array.) I don't know how to edit the lambda expression to check against the loop.
var activities = await _restClientTaxonomy.GetTaxonomyFullAsync(TAXONOMY_CLASSIFICATIONID_FOR_ACTIVITY);
var activityTypes = await _restClientTaxonomy.GetTaxonomyFullAsync(TAXONOMY_CLASSIFICATIONID_FOR_ACTIVITY_TYPES);
var documentEventxx = activities.Select(type => type.Id);
long [] allowedA = new long []{ 7137, 40385637};
long [] allowedB = new long []{ 7137, 40385637};
foreach (long value in documentEventxx)
{
foreach (var item in allowed)
{
if (item == value) {
//These are the values I am looking for -> values that are part of the documentEventxx and allowedB.
}
}
}
var result = activityTypes.Select(type => new CategoryViewModel
{
Id = type.Id,//This is where I want to add only items that are in the allowedA array
Text = type.Name,
Types = activities.Where(a => a.ParentId == type.Id).Select(t => new TaxonomyMemberTextItem
{
Id = t.Id, //This is where I want to add only items that are in the allowedB array
Text = t.Name
}).ToList()
}).ToArray();
I have been reading about lambda expressions and foreach loops so please don't just post a random link.
Thanks in advance.
Filter the values before Selecting.
activityTypes.Where(x=>allowedA.Contains(x.Id)).Select(type => new CategoryViewModel
{
Id = type.Id,
Text = type.Name,
Types = activities.Where(a => a.ParentId == type.Id && allowedB.Contains(a.Id)).Select(t => new TaxonomyMemberTextItem
{
Id = t.Id,
Text = t.Name
}).ToList()
})
To filter you use .Where. You .Select to create a list of new types. So in order to filter, then create the lists of objects you want:
var result = activityTypes.Where(type=>isAllowed(type.Id)).Select(type => new CategoryViewModel
{
Id = type.Id,//This is where I want to add only items that are in the allowedA array
Text = type.Name,
Types = activities.Where(a => a.ParentId == type.Id&&isAllowed(a.Id)).Select(t => new TaxonomyMemberTextItem
{
Id = t.Id, //This is where I want to add only items that are in the allowedB array
Text = t.Name
}).ToList()
}).ToArray();

How to return Distinct Row using LINQ

I have two rows which have all the data same except one column.
I want to show only one row on the UI but one row which has different data should be shown as comma seperated values.
Sample Data
PricingID Name Age Group
1 abc 56 P1
1 abc 56 P2
Output should be :
PricingID Name Age Group
1 abc 56 P1,P2
I am using this approach but it is not working , it gives me two rows only but data i am able to concatenate with comma.
List<PricingDetailExtended> pricingDetailExtendeds = _storedProcedures.GetPricingAssignment(pricingScenarioName, regionCode, productCode, stateCode, UserId, PricingId).ToList();
var pricngtemp = pricingDetailExtendeds.Select(e => new
{
PricingID = e.PricingID,
OpportunityID = e.OpportunityID,
ProductName = e.ProductName,
ProductCD = e.ProductCD
});
pricingDetailExtendeds.ForEach(e=>
{
e.ProductCD = string.Join(",",string.Join(",", (pricngtemp.ToList().Where(p => p.PricingID == e.PricingID).Select(k => k.ProductCD).ToArray())).Split(',').Distinct().ToArray());
e.OpportunityID =string.Join(",", string.Join(",", (pricngtemp.ToList().Where(p => p.PricingID == e.PricingID).Select(k => k.OpportunityID).ToArray())).Split(',').Distinct().ToArray());
e.ProductName =string.Join(",", string.Join(",", (pricngtemp.ToList().Where(p => p.PricingID == e.PricingID).Select(k => k.ProductName).ToArray())).Split(',').Distinct().ToArray());
}
);
// pricingDetailExtendeds = GetUniquePricingList(pricingDetailExtendeds);
return pricingDetailExtendeds.Distinct().AsEnumerable();
Any body can suggest me better approach and how to fix this issue ?
Any help is appreciated.
You want to use the GroupBy linq function.
I then use the String.Join function to make the groups comma seperated.
So something like this:
var pricingDetailExtendeds = new[]
{
new
{
PricingID = 1,
Name = "abc",
Age = 56,
Group = "P1"
},
new
{
PricingID = 1,
Name = "abc",
Age = 56,
Group = "P2"
}
};
var pricngtemp =
pricingDetailExtendeds.GroupBy(pde => new {pde.PricingID, pde.Name, pde.Age})
.Select(g => new {g.Key, TheGroups = String.Join(",", g.Select(s => s.Group))}).ToList();
You can easily extrapolate this to the other fields.
To return the PricingDetailExtended, the just create it in the select. So something like this
.Select(g => new PricingDetailExtended {
PricingID = g.Key.PricingId,
TheGroups = String.Join(",", g.Select(s => s.Group))
}).ToList();
You won't have the field TheGroups though, so just replace that field with the proper one.
An example of what I was describing in my comment would be something along the lines of the following. I would expect this to be moved into a helper function.
List<PriceDetail> list = new List<PriceDetail>
{
new PriceDetail {Id = 1, Age = 56, Name = "abc", group = "P1"},
new PriceDetail {Id = 1, Age = 56, Name = "abc", group = "P2"},
new PriceDetail {Id = 2, Age = 56, Name = "abc", group = "P1"}
};
Dictionary<PriceDetailKey, StringBuilder> group = new Dictionary<PriceDetailKey, StringBuilder>();
for (int i = 0; i < list.Count; ++i)
{
var key = new PriceDetailKey { Id = list[i].Id, Age = list[i].Age, Name = list[i].Name };
if (group.ContainsKey(key))
{
group[key].Append(",");
group[key].Append(list[i].group);
}
else
{
group[key] = new StringBuilder();
group[key].Append(list[i].group);
}
}
List<PriceDetail> retList = new List<PriceDetail>();
foreach (KeyValuePair<PriceDetailKey, StringBuilder> kvp in group)
{
retList.Add(new PriceDetail{Age = kvp.Key.Age, Id = kvp.Key.Id, Name = kvp.Key.Name, group = kvp.Value.ToString()});
}
you could even convert the final loop into a LINQ expression like:
group.Select(kvp => new PriceDetail {Age = kvp.Key.Age, Id = kvp.Key.Id, Name = kvp.Key.Name, group = kvp.Value.ToString()});
Its worth noting you could do something similar without the overhead of constructing new objects if, for example, you wrote a custom equality comparer and used a list instead of dictionary. The upside of that is that when you were finished, it would be your return value without having to do another iteration.
There are several different ways to get the results. You could even do the grouping in SQL.

Nested Group by LINQ

I am unable to solve this problem with the LINQ Query.
So we have the table structure as follows:
Id || bug_category || bug_name || bug_details || bug_priority
I want to group by bug_category first. For each bug_category, I want to in turn group by bug__priority.
So basically I want something like :
bug_category = AUDIO :: No of BUGS --> Critical = 3, Medium = 2 and Low = 7 bugs.
bug_category = VIDEO :: No of BUGS --> Critical = 5, Medium = 1 and Low = 9 bugs.
The below query returns all unique combinations of category AND customer_priority:
(where RawDataList is simply a List of data which has the above mentioned structure )
var ProceesedData = from d in RawDataList
group d by new { d.bug_category, d.bug_priority } into g
select new
{
g.Key.bug_category,
g.Key.bug_priority
};
The below query returns the category followed by a list of records in that category:
var ProceesedData = from d in RawDataList
group d by d.bug_category into g
select new { g.Key, records = g
};
But I am unable to proceed further as ProcessedData(the return variable) is an unknown type. Any thoughts on this?
This is an easier way to accomplish nested groupings. I've tested it for in memory collections, whether or not your particular DB provider will handle it well might vary, or whether it performs well is unknown.
Assuming you had two properties, and wanted to group by both State and Country:
var grouped = People
.GroupBy(l => new { l.State, l.Country})//group by two things
.GroupBy(l=> l.Key.Country)//this will become the outer grouping
foreach(var country in grouped)
{
foreach(var state in country)
{
foreach(var personInState in state)
{
string description = $"Name: {personInState.Name}, State: {state.StateCode}, Country: {country.CountryCode}";
...
}
}
}
I suspect you want (names changed to be more idiomatic):
var query = from bug in RawListData
group bug by new { bug.Category, bug.Priority } into grouped
select new {
Category = grouped.Key.Category,
Priority = grouped.Key.Priority,
Count = grouped.Count()
};
Then:
foreach (var result in query)
{
Console.WriteLine("{0} - {1} - {2}",
result.Category, result.Priority, result.Count);
}
Alternatively (but see later):
var query = from bug in RawListData
group bug by new bug.Category into grouped
select new {
Category = grouped.Category,
Counts = from bug in grouped
group bug by grouped.Priority into g2
select new { Priority = g2.Key, Count = g2.Count() }
};
foreach (var result in query)
{
Console.WriteLine("{0}: ", result.Category);
foreach (var subresult in result.Counts)
{
Console.WriteLine(" {0}: {1}", subresult.Priority, subresult.Count);
}
}
EDIT: As noted in comments, this will result in multiple SQL queries. To obtain a similar result structure but more efficiently you could use:
var dbQuery = from bug in RawListData
group bug by new { bug.Category, bug.Priority } into grouped
select new {
Category = grouped.Key.Category,
Priority = grouped.Key.Priority,
Count = grouped.Count()
};
var query = dbQuery.ToLookup(result => result.Category,
result => new { result.Priority, result.Count };
foreach (var result in query)
{
Console.WriteLine("{0}: ", result.Key);
foreach (var subresult in result)
{
Console.WriteLine(" {0}: {1}", subresult.Priority, subresult.Count);
}
}
I think you're searching something like that:
var processedData =
rawData.GroupBy(bugs => bugs.bug_category,
(category, elements) =>
new
{
Category = category,
Bugs = elements.GroupBy(bugs => bugs.bug_priority,
(priority, realbugs) =>
new
{
Priority = priority,
Count = realbugs.Count()
})
});
foreach (var data in processedData)
{
Console.WriteLine(data.Category);
foreach (var element in data.Bugs)
Console.WriteLine(" " + element.Priority + " = " + element.Count);
}
You can do it like this
var retList = (from dbc in db.Companies
where dbc.IsVerified && dbc.SellsPCBs && !dbc.IsDeleted && !dbc.IsSpam && dbc.IsApproved
select new
{
name = dbc.CompanyName,
compID = dbc.CompanyID,
state = dbc.State,
city = dbc.City,
businessType = dbc.BusinessType
}).GroupBy(k => k.state).ToList();
List<dynamic> finalList = new List<dynamic>();
foreach (var item in retList)
{
finalList.Add(item.GroupBy(i => i.city));
}

LINQ sum collection of items to return object with results (multiple columns)

I know I can do the below with a foreach but was wondering if there is a clean and "sexier" way of doing this with LINQ.
public class item
{
public int total { get; set; }
public int net { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<item> items = new List<item>()
{
new item() { total = 123, net = 423},
new item() { total = 432, net = 54},
new item() { total = 33, net = 57654},
new item() { total = 33, net = 423},
new item() { total = 3344, net = 423},
new item() { total = 123, net = 423},
new item() { total = 123, net = 98},
new item() { total = 123, net = 867},
new item() { total = 123, net = 876},
new item() { total = 123, net = 423},
new item() { total = 123, net = 543},
new item() { total = 543, net = 345},
};
item i = new item();
foreach (var item in items)
{
i.net += item.net;
i.total += item.total;
}
}
}
What I would like to do is, for a given list of objects sum each of the columns / fields and return one single object with the sum of each value.
I tried:
var result = (from e in items
select new
{
NET_GRAND = e.net,
TOTAL_GRAND = e.total
}).ToList();
And variations on the below but with no luck:
var result = (from t in items
group t by new {t.net, t.total}
into grp
select new
{
NET_GRAND = grp.Sum(t => t.net),
TOTAL_GRAND = grp.Sum(t => t.total)
}).GroupBy(x => new { x.NET_GRAND, x.TOTAL_GRAND }).ToList();
EDIT
should have pointed out that efficiency is important here as well as sexiness.
If you don't care about iterating the list twice,
var i = new item
{
net = items.Sum(it => it.net),
total = items.Sum(it => it.total)
};
If you do care about iterating the list twice (as you might be if you were doing this for an IEnumerable of unknown origin),
var i = items.Aggregate(new item(),
(accumulator, it) =>
new item
{
net = accumulator.net + it.net,
total = accumulator.total + it.total
}
);
It looks like you really want:
var result = new {
NetGrand = items.Sum(t => t.net),
TotalGrand = items.Sum(t => t.total)
};
On the other hand, I'd probably just separate those into two different local variables:
var netGrand = items.Sum(t => t.net);
var totalGrand = items.Sum(t => t.total);
Of course this iterates over the list twice, but in most cases I'd expect that not to be noticeable.
item totals = new item
{
net = items.Sum(i => i.net),
total = items.Sum(i => i.total)
};
But keep in mind that this query will enumerate a list two times, so for a large list this would not so efficient as old good single foreach loop.
var item = new item();
item.net = items .Sum(x=>x.net);
item.total = items.Sum(x=>x.total);
Use the foreach loop. You state that you are concerned about efficiency, and even if you weren't there is no reason to write it using Linq just to be using Linq.
One of the things we discover as we get lots more experience with programs is that just because something is done "the old way" does not make it bad. And converting to the new-wiz-bang method does not make it better. In fact, if you have code working the old way, "upgrading" is a cause for injection of defects, with in many cases no advantages.
At best, this Linq method will take 2X longer to compute.
var i = new item
{
net = items.Sum(it => it.net),
total = items.Sum(it => it.total)
};
Not sure about the aggregate method, but clearly it will take longer.
I believe that you can accomplish this using LINQ without iterating twice by using GroupBy with a constant value for the key (in this case I use 1).
item totals = items.GroupBy(i => 1).Select(g => new item()
{
net = g.Sum(i => i.net),
total = g.Sum(i => i.total)
}).Single();

Categories