How to find value from a Dictionary<int, IEnumerable<Struct>> - c#

I have a dictionary, salaryFitmentDictionary which I would like to query (linq or lambda) based on example: where employeedId = 1 and EarningDeductionId = 145 and get the value of the balance, EDBalance.
How would I achieve this?
var balance = salaryFitmentDictionary.Where...
Dictionary<int, IEnumerable<SalaryFitmentInfoMonth>> salaryFitmentDictionary = new Dictionary<int, IEnumerable<SalaryFitmentInfoMonth>>();
employeeIdList.ToList().ForEach(employeedId =>
{
var perEmployeeFitments = from pf in _db.PayFitments.AsEnumerable()
join ed in _db.EarningDeductions.AsEnumerable()
on pf.EarningDeductionId equals ed.EarningDeductionId
where pf.EmployeeId == employeedId
select new SalaryFitmentInfoMonth
{
EDId = pf.EarningDeductionId,
EDAmount = pf.Amount,
EDBalance = pf.Balance.GetValueOrDefault(),
EDType = ed.EDType,
IsTaxable = ed.IsTaxable,
IsBenefit = ed.IsBenefit,
IsLoan = ed.IsLoan,
IsAdvance = ed.IsAdvance,
Limit = ed.TaxIfMoreThan.GetValueOrDefault()
};
salaryFitmentDictionary.Add(employeedId, perEmployeeFitments);
});
public struct SalaryFitmentInfoMonth
{
public int EDId { get; set; }
public decimal EDAmount { get; set; }
public decimal? EDBalance { get; set; }
public EarnDeduct EDType { get; set; }
public bool IsTaxable { get; set; }
public bool IsBenefit { get; set; }
public bool IsLoan { get; set; }
public bool IsAdvance { get; set; }
public decimal? Limit { get; set; }
}

IEnumerable<SalaryFitmentInfoMonth> salaries = salaryFitmentDictionary[1];
SalaryFitmentInfoMonth salary = salaries.FirstOrDefault(s => s.EDId == 45);
You should handle the case that salaryFitmentDictionary doesn't contain one with this ID. So you could use TryGetValue instead. If no salary has this EDId FirstOrDefault returns null.
So here's the safer version:
IEnumerable<SalaryFitmentInfoMonth> salaries;
if(salaryFitmentDictionary.TryGetValue(1, out salaries))
{
SalaryFitmentInfoMonth salary = salaries.FirstOrDefault(s => s.EDId == 45);
if(salary != null)
{
// do something ...
}
}
If you expect more than one match you could use Enumerable.Where instead of FirstOrDefault.

You could use SelectMany method in LINQ method syntax:
Int32 id = 1;
Int32 edId = 147;
var result = salaryFitmentDictionary.
Where((pair) => pair.Key == id ).
SelectMany((pair) =>
pair.Value.Where((perEmployeeFitment) => perEmployeeFitment.EDId == edId)).
Select(perEmployeeFitment => perEmployeeFitment.EDBalance).
Single();
Or in query syntax:
Int32 id = 1;
Int32 edId = 147;
var result = (from pair in salaryFitmentDictionary
from perEmployeeFitment in pair.Value
where pair.Key == id
where perEmployeeFitment.EDId == edId
select perEmployeeFitment.EDBalance).Single();

Related

Sorting and Updating a Generic List of Object based on a Sub Object

I have the following objects:
public class TestResult
{
public string SectionName { get; set; }
public int Score { get; set; }
public int MaxSectionScore { get; set; }
public bool IsPartialScore { get; set; }
public string Name { get; set; }
public int NumberOfAttempts { get; set; }
}
public class TestResultGroup
{
public TestResultGroup()
{
Results = new List<TestResult>();
Sections = new List<string>();
}
public List<TestResult> Results { get; set; }
public List<string> Sections { get; set; }
public string Name { get; set; }
public int Rank { get; set; }
}
So, a TestResultGroup can have any number of results of type TestResult. These test results only differ by their SectionName.
I have a List<TestResultGroup> which I need to sort into descending order based on a score in the Results property, but only when Results has an item whos SectionName = "MeanScore" (if it doesnt have this section we can assume a score of -1). How would I go about ordering the list? Ideally I would also like to apply the result of this ordering to the Rank property.
Many Thanks
List<TestResultGroup> groups = ...
// group test result groups by the same score and sort
var sameScoreGroups = groups.GroupBy(
gr =>
{
var meanResult = gr.Results.FirstOrDefault(res => res.SectionName == "MeanScore");
return meanResult != null ? meanResult.Score : -1;
})
.OrderByDescending(gr => gr.Key);
int rank = 1;
foreach (var sameScoreGroup in sameScoreGroups)
{
foreach (var group in sameScoreGroup)
{
group.Rank = rank;
}
rank++;
}
// to obtain sorted groups:
var sortedGroups = groups.OrderByDescending(gr => gr.Rank).ToArray();
Or even write one expression with a side effect:
List<TestResultGroup> groups = ...
int rank = 1;
var sortedGroups = groups
.GroupBy(
gr =>
{
var meanResult = gr.Results.FirstOrDefault(res => res.SectionName == "MeanScore");
return meanResult != null ? meanResult.Score : -1;
})
.OrderByDescending(grouping => grouping.Key)
.SelectMany(grouping =>
{
int groupRank = rank++;
foreach (var group in grouping)
{
group.Rank = groupRank;
}
return grouping;
})
.ToArray(); // or ToList

what is wrong with my GroupBy Query

Hello all what is wrong with my GroupBy query ?
I have following class:
public class AssembledPartsDTO
{
public int PID { get; set; }
public McPosition Posiotion { get; set; }
public string Partnumber { get; set; }
public string ReelID { get; set; }
public int BlockId { get; set; }
public List<string> References { get; set; }
}
I am trying to perform following query:
assembledPcb.AssembledParts.GroupBy(entry => new
{
entry.PID,
entry.Posiotion.Station,
entry.Posiotion.Slot,
entry.Posiotion.Subslot,
entry.Partnumber,
entry.ReelID,
entry.BlockId
}).
Select( (key , val )=> new AssembledPartsDTO
{
BlockId = key.Key.BlockId,
PID = key.Key.PID,
Partnumber = key.Key.Partnumber,
ReelID = key.Key.ReelID,
Posiotion = new McPosition(key.Key.Station, key.Key.Slot, key.Key.Subslot),
References = val <-- ????
})
But the val that I have there is of type int and not the val of grouping that I can do there val.SelectMany(v => v).ToList(); any idea what is wrong in my code ?
The second parameter of Enumerable.Select is the index of the item in the sequence. So in this case it is the (zero based) number of the group. You just want to select the group, you don't need it's index:
var result = assembledPcb.AssembledParts.GroupBy(entry => new
{
entry.PID,
entry.Posiotion.Station,
entry.Posiotion.Slot,
entry.Posiotion.Subslot,
entry.Partnumber,
entry.ReelID,
entry.BlockId
})
.Select(g => new AssembledPartsDTO
{
BlockId = g.Key.BlockId,
PID = g.Key.PID,
Partnumber = g.Key.Partnumber,
ReelID = g.Key.ReelID,
Posiotion = new McPosition(g.Key.Station, g.Key.Slot, g.Key.Subslot),
References = g.SelectMany(entry => entry.References)
.Distinct()
.ToList()
});
(assuming that you want a list of distinct references)
Side-Note: you have a typo at the property-name: Posiotion

Efficient way to call .Sum() on multiple properties

I have a function that uses LINQ to get data from the database and then I call that function in another function to sum all the individual properties using .Sum() on each individual property. I was wondering if there is an efficient way to sum all the properties at once rather than calling .Sum() on each individual property. I think the way I am doing as of right now, is very slow (although untested).
public OminitureStats GetAvgOmnitureData(int? fnsId, int dateRange)
{
IQueryable<OminitureStats> query = GetOmnitureDataAsQueryable(fnsId, dateRange);
int pageViews = query.Sum(q => q.PageViews);
int monthlyUniqueVisitors = query.Sum(q => q.MonthlyUniqueVisitors);
int visits = query.Sum(q => q.Visits);
double pagesPerVisit = (double)query.Sum(q => q.PagesPerVisit);
double bounceRate = (double)query.Sum(q => q.BounceRate);
return new OminitureStats(pageViews, monthlyUniqueVisitors, visits, bounceRate, pagesPerVisit);
}
private IQueryable<OminitureStats> GetOmnitureDataAsQueryable(int? fnsId, int dateRange)
{
var yesterday = DateTime.Today.AddDays(-1);
var nDays = yesterday.AddDays(-dateRange);
if (fnsId.HasValue)
{
IQueryable<OminitureStats> query = from o in lhDB.omniture_stats
where o.fns_id == fnsId
&& o.date <= yesterday
&& o.date > nDays
select new OminitureStats (
o.page_views.GetValueOrDefault(),
o.monthly_unique.GetValueOrDefault(),
o.visits.GetValueOrDefault(),
(double)o.bounce_rate.GetValueOrDefault()
);
return query;
}
return null;
}
public class OminitureStats
{
public OminitureStats(int PageViews, int MonthlyUniqueVisitors, int Visits, double BounceRate)
{
this.PageViews = PageViews;
this.MonthlyUniqueVisitors = MonthlyUniqueVisitors;
this.Visits = Visits;
this.BounceRate = BounceRate;
this.PagesPerVisit = Math.Round((double)(PageViews / Visits), 1);
}
public OminitureStats(int PageViews, int MonthlyUniqueVisitors, int Visits, double BounceRate, double PagesPerVisit)
{
this.PageViews = PageViews;
this.MonthlyUniqueVisitors = MonthlyUniqueVisitors;
this.Visits = Visits;
this.BounceRate = BounceRate;
this.PagesPerVisit = PagesPerVisit;
}
public int PageViews { get; set; }
public int MonthlyUniqueVisitors { get; set; }
public int Visits { get; set; }
public double PagesPerVisit { get; set; }
public double BounceRate { get; set; }
}
IIRC you can do all the sums in one go (as long as the query is translated to SQL) with
var sums = query.GroupBy(q => 1)
.Select(g => new
{
PageViews = g.Sum(q => q.PageViews),
Visits = g.Sum(q => q.Visits),
// etc etc
})
.Single();
This will give you one object which contains all the sums as separate properties.
I found out why it was throwing the NotSupportedException. I learned that Linq to Entity does not support constructors with parameters, So deleted the constructors and made changes in my query. I am a novice C# programmer, so let me know if my solution could be improved, but as of right now it is working fine.
public class OminitureStats
{
public int PageViews { get; set; }
public int MonthlyUniqueVisitors { get; set; }
public int Visits { get; set; }
public double PagesPerVisit { get; set; }
public double BounceRate { get; set; }
}
private IQueryable<OminitureStats> GetOmnitureDataAsQueryable(int? fnsId, int dateRange)
{
var yesterday = DateTime.Today.AddDays(-1);
var nDays = yesterday.AddDays(-dateRange);
if (fnsId.HasValue)
{
IQueryable<OminitureStats> query = from o in lhDB.omniture_stats
where o.fns_id == fnsId
&& o.date <= yesterday
&& o.date > nDays
select new OminitureStats() {
o.page_views.GetValueOrDefault(),
o.monthly_unique.GetValueOrDefault(),
o.visits.GetValueOrDefault(),
(double)o.bounce_rate.GetValueOrDefault()
};
return query;
}
return null;
}

How to extract result of Linq Expression?

My result Expression is
var result = dtFields.AsEnumerable().Join(dtCDGroup.AsEnumerable(),
fieldList=>fieldList.Field<string>("CDGroupID"),
cd=>cd.Field<string>("CDGroupID"),
(fieldList,cd) => new
{
FieldID = fieldList.Field<string>("FieldID"),
Name = cd.Field<string>("Name"),
CDCaption = fieldList.Field<string>("CDCaption"),
Priority = ((cd.Field<string>("Priority") == null) ? 99 : cd.Field<int>("Priority")),
fldIndex = fieldList.Field<string>("fldIndex")
}).OrderBy(result => result.Priority).ThenBy(result => result.fldIndex);
Casting above result to array or list throws an invalid cast exception.
How can extract result of above expression?
Add .ToArray() or .ToList() call respectively
Try to add a strongly typed type:
public class NewModule
{
public int FieldID { get; set; }
public string Name { get; set; }
public string CDCaption { get; set; }
public int Priority { get; set; }
public int fldIndex { get; set; }
}
instead of the anonymous type then you could use ToList<NewModule>() like this:
var result = dtFields.AsEnumerable().Join(dtCDGroup.AsEnumerable(),
fieldList=>fieldList.Field<string>("CDGroupID"),
cd=>cd.Field<string>("CDGroupID"),
(fieldList,cd) => new NewModule
{
FieldID = fieldList.Field<string>("FieldID"),
Name = cd.Field<string>("Name"),
CDCaption = fieldList.Field<string>("CDCaption"),
Priority = ((cd.Field<string>("Priority") == null) ? 99 : cd.Field<int>("Priority")),
fldIndex = fieldList.Field<string>("fldIndex")
}).OrderBy(result => result.Priority)
.ThenBy(result => result.fldIndex)
.ToList<NewModule>();

can't select into class using linq

I have a query that works fine when using an anonymous type but as soon as I try to un-anonymize it it fails to select all values into the class.
here is the linq i'm using (in combination with Subsonic 3):
var producten = (from p in Premy.All()
join pr in Producten.All() on p.dekking equals pr.ID
where p.kilometragemax >= 10000 &&
p.CCmin < 3000 &&
p.CCmax >= 3000 &&
p.leeftijdmax >= DateTime.Today.Subtract(car.datumEersteToelating).TotalDays / 365
group p by new { pr.ID, pr.Naam, pr.ShortDesc, pr.LongDesc } into d
select new
{
ID = d.Key.ID,
Dekking = d.Key.Naam,
ShortDesc = d.Key.ShortDesc,
LongDesc = d.Key.LongDesc,
PrijsAlgemeen = d.Min(x => x.premie),
PrijsAlgemeenMaand = d.Min(x => x.premie),
PrijsMerkdealerMaand = d.Min(x => x.premie),
PrijsMerkdealer = d.Min(x => x.premie)
}).ToList();
When I change it to:
List<QuotePremies> producten = (from p in Premy.All()
join pr in Producten.All() on p.dekking equals pr.ID
where p.kilometragemax >= 10000 &&
p.CCmin < 3000 &&
p.CCmax >= 3000 &&
p.leeftijdmax >= DateTime.Today.Subtract(car.datumEersteToelating).TotalDays / 365
group p by new { pr.ID, pr.Naam, pr.ShortDesc, pr.LongDesc } into d
select new QuotePremies
{
ID = d.Key.ID,
Dekking = d.Key.Naam,
ShortDesc = d.Key.ShortDesc,
LongDesc = d.Key.LongDesc,
PrijsAlgemeen = d.Min(x => x.premie),
PrijsAlgemeenMaand = d.Min(x => x.premie),
PrijsMerkdealerMaand = d.Min(x => x.premie),
PrijsMerkdealer = d.Min(x => x.premie)
}).ToList();
in combination with this class:
public class QuotePremies
{
public byte ID { get; set; }
public string Dekking { get; set; }
public string ShortDesc { get; set; }
public string LongDesc { get; set; }
public decimal PrijsAlgemeen { get; set; }
public decimal PrijsAlgemeenMaand { get; set; }
public decimal PrijsMerkdealer { get; set; }
public decimal PrijsMerkdealerMaand { get; set; }
}
it doesn't give me an error but all values in the class are 0 except for QuotePremies.ID, QuotePremies.ShortDesc and QuotePremies.LongDesc. No clue why that happens.
See if using conversion helps
PrijsAlgemeen = Convert.ToDecimal(d.Min(x => x.premie))
I believe the problem has to do with casting. Why not write and extension method for IEnumberable which would take this query result and return a collection of List. It could look something like this:
public static class Extensions
{
// extends IEnumerable to allow conversion to a custom type
public static TCollection ToMyCustomCollection<TCollection, T>(this IEnumerable<T> ienum)
where TCollection : IList<T>, new()
{
// create our new custom type to populate and return
TCollection collection = new TCollection();
// iterate over the enumeration
foreach (var item in ienum)
{
// add to our collection
collection.Add((T)item);
}
return collection;
}
}
Thanks to kek444 for helping me with a similar problem

Categories

Resources