Get one child element per group - c#

I am getting, with EF6, one advert by position as follows:
var adverts = context.Adverts
.Include(x => x.Files)
.Where(x => x.Position <= 32)
.OrderBy(x => Guid.NewGuid())
.GroupBy(x => x.Position)
.ToDictionary(x => x.Key, x => x.First());
I get one advert per group and each advert has one file.
But what I really want is a file for each advert so I tried:
var adverts = context.Adverts
.Include(x => x.Files)
.Where(x => x.Position <= 32)
.OrderBy(x => Guid.NewGuid())
.GroupBy(x => x.Position)
.ToDictionary(x => x.Key, x => x.First().Files);
In this case the value of the dictionary is empty.
Any idea how to solve this?

Related

EFCore Query and selection

Hey I am trying to query and find a object that looks something like this:
I have already result
I have id that is quiz id.
So i have both Ids.
var singlequiz = await _context.Quizzes
.Include(q => q.Questions)
.ThenInclude(question => question.Options)
.Include(qz => qz.Results)
.Where(b => b.Id == id)
.Select(c => c.Results.Select(z => z.Id == result.Id))
.FirstOrDefaultAsync();
What I am trying to do here is : find an object with id X, then that object with id X has a list of results, I want to get the list, BUT with only the one element that has id of result.Id.
What am i doing wrong?
EDIT:
Used this ATM
var singlequiz = await _context.Quizzes
.Include(x => x.Questions)
.ThenInclude(x => x.Options)
.Include(x => x.Results)
.FirstOrDefaultAsync(x => x.Id == id);
var singleQuizElement = singlequiz.Results.Where(x => x.Id == result.Id).ToList();
singlequiz.Results = singleQuizElement;
Try this :
var singlequiz = await _context.Quizzes
.Include(x => x.Questions)
.ThenInclude(x => x.Options)
.Include(x => x.Results)
.FirstOrDefaultAsync(x=> x.Id == id && x.Results.Any(y => y.Id == result.Id));

Dictionary groupings and sum causes An item with the same key has already been added

I have a scenario where in case there is a specific boolean value satisfied (office_debit_total line) I can get amount directly from a column otherwise I need to calculate it by grouping some specific values, here's the code:
var result = products.Select(p => new ResponseDto()
{
customer_id = p.CustomerId,
office_debit_date = p.OfficeDebitDate.Value.ToString(),
office_debit_id = p.OfficeDebitId.ToString(),
office_debit_total = p.OfficeEnum == SomeEnum.ValueType ? p.OfficeAmount.ToString() : totalAmounts[p.OfficeDebitId].ToString(),
payment_method = p.PaymentMethod.Value.ToString(),
}).ToList();
As it's possible to be seen office_debit_total is calculated depending on enum value, and here's dictionary that I'm using to get grouped data:
Dictionary<string, decimal> totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => new { p.OfficeDebitId, p.OfficeDebitDate, p.PaymentMethod })
.ToDictionary(x => x.Key.OfficeDebitId, x => x.Sum(p => p.Amount));
But I have receiving following error message:
An item with the same key has already been added.
I've tried writing .ToLookup instead of .ToDictionary but that didn't helped me..
Thanks guys
Cheers
If your dictionary has only OfficeDebitId as key then you need to group by only by it:
var totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => p.OfficeDebitId)
.ToDictionary(x => x.Key, x => x.Sum(p => p.Amount));
or use full anonymous object as key:
var totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => new { p.OfficeDebitId, p.OfficeDebitDate, p.PaymentMethod })
.ToDictionary(x => x.Key, x => x.Sum(p => p.Amount));
Or with value tuple as key:
var totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => (p.OfficeDebitId, p.OfficeDebitDate, p.PaymentMethod))
.ToDictionary(x => x.Key, x => x.Sum(p => p.Amount));
Why not this:
Dictionary<string, decimal> totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => p.OfficeDebitId)
.ToDictionary(x => x.Key, x => x.Sum(p => p.Amount));
You might need it in this way (You can use value tuple):
Dictionary<(string OfficeDebitId, System.DateTime? OfficeDebitDate, Enumerations.PaymentMethod? PaymentMethod), decimal> totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => new { p.OfficeDebitId, p.OfficeDebitDate, p.PaymentMethod })
.ToDictionary(x => (x.Key.OfficeDebitId, x.Key.OfficeDebitDate, x.Key.PaymentMethod ), x => x.Sum(p => p.Amount));

C# Linq Lambda for group by multiple columns select max

I want to translate this into lambda syntax and can't seem to get it to work:
Grouping by two columns, select max on a different column, return list of complete complex object.
I am writing more text here to get past the validation on this form. How much text is needed until I am allowed to post this?
_clientpolicies = (from policy in
_reply.CommercialInsuredGroupWithPolicyTerm.InsuredWithPolicyTerm.SelectMany(x => x.PolicyTerm)
.Where(x => !(string.IsNullOrWhiteSpace(x.PolicyNumber) && string.IsNullOrWhiteSpace(x.ControlNumber)))
.Where(x => x.Insured.DNBAccountNumber == _client.LookupID)
group policy by
new
{
PolicyReference = GetPolicyReference(policy),
PolicyType = policy.ProductInformation.PolicyTypeCode
}
into g
let maxPolicyInception = g.Max(p => p.InceptionDate)
from policyGroup in g
where policyGroup.InceptionDate == maxPolicyInception
select policyGroup).ToList();
I dont think there's a way of doing it in one line. So there's my try :
policyGroups=
_reply.CommercialInsuredGroupWithPolicyTerm.InsuredWithPolicyTerm
.SelectMany(x => x.PolicyTerm)
.Where(x => !(string.IsNullOrWhiteSpace(x.PolicyNumber) && string.IsNullOrWhiteSpace(x.ControlNumber)))
.Where(x => x.Insured.DNBAccountNumber == _client.LookupID)
.GroupBy(x => GetPolicyReference(x))
.ThenBy(x => x.ProductInformation.PolicyTypeCode)
.ToList();
var maxPolicyInception = policyGroups.Max(p => p.InceptionDate);
_clientpolicies = policyGroups
.Where(g => g.InceptionDate == maxPolicyInception)
.ToList();
_clientpolicies =
_reply.CommercialInsuredGroupWithPolicyTerm.InsuredWithPolicyTerm.SelectMany(x => x.PolicyTerm)
.Where(x => !(string.IsNullOrWhiteSpace(x.PolicyNumber) && string.IsNullOrWhiteSpace(x.ControlNumber)))
.Where(x => x.Insured.DNBAccountNumber == _client.LookupID)
.GroupBy(x =>
new
{
PolicyReference = GetPolicyReference(x),
PolicyType = x.ProductInformation.PolicyTypeCode
},
(key, g) => g.OrderByDescending(gx => gx.InceptionDate).First()

How to speed up grouping and add to dictionary

I have code:
Dictionary<long, List<Data>> daDictTmp = retValTmp
.Where(w => w.IdData.HasValue)
.GroupBy(d => d.IdData.Value)
.ToDictionary(x => x.Key, x => x.ToList());
Is there any other way to do this operation faster?

Linq query fine tuning

Query logic is Grouping the items by Id and Ordering it by Id. Then inside grouped items Ordering by Item1 then by Item2.
Linq query below,
var group1Items = MyList.GroupBy(g => g.Id)
.Where(w => w.Any(a => a.Code = 1)
.Select(s => new
{ key = s.Key,
items = s.OrderBy(o => o.Item1)
.ThenBy(t => t.Item2)
}
)
.OrderBy(o => o.Id)
.SelectMany(sm => sm.Items).ToList();
var group2Items = MyList.GroupBy(g => g.Id)
.Where(w => w.Any(a => a.Code = 2)
.Select(s => new
{ key = s.Key,
items = s.OrderBy(o => o.Item1)
.ThenBy(t => t.Item2)
}
)
.OrderBy(o => o.Id)
.SelectMany(sm => sm.Items).ToList();
MyList.Clear();
MyList.InsertRange(Mylist.Count, group1Items);
MyList.InsertRange(Mylist.Count, group2Items);
In the above two queries, only difference is Where condition. Is it possible to rewrite into single query?
Single query:
var groupItems = MyList.GroupBy(g => g.Id)
.Where(w => w.Any(a => a.Code == 1 || a.Code == 2)
.Select(s => new
{ key = s.Key,
items = s.OrderBy(o => o.Item1)
.ThenBy(t => t.Item2)
}
)
.OrderBy(o => o.Id)
.SelectMany(sm => sm.Items).ToList();
If Code 1 must come before Code 2:
var groupItems = MyList.GroupBy(g => g.Id)
.Where(w => w.Any(a => a.Code == 1)
.Select(s => new
{ key = s.Key,
items = s.OrderBy(o => o.Item1)
.ThenBy(t => t.Item2)
}
)
.OrderBy(o => o.Id)
.SelectMany(sm => sm.Items)
.Union(MyList.GroupBy(g => g.Id)
.Where(w => w.Any(a => a.Code == 2)
.Select(s => new
{ key = s.Key,
items = s.OrderBy(o => o.Item1)
.ThenBy(t => t.Item2)
}
)
.OrderBy(o => o.Id)
.SelectMany(sm => sm.Items)).ToList();
If you only want to avoid code duplication it may be easiest to capture a variable holding the code by which to filter.
int code = 0; // initialize with any value
var groupItems = MyList.GroupBy(g => g.Id)
.Where(w => w.Any(a => a.Code == code)
.Select(s => new
{
key = s.Key,
items = s.OrderBy(o => o.Item1)
.ThenBy(t => t.Item2)
})
.OrderBy(o => o.Id)
.SelectMany(sm => sm.Items); // No ToList() here!
MyList.Clear();
code = 1;
MyList.InsertRange(Mylist.Count, groupItems.ToList());
code = 2;
MyList.InsertRange(Mylist.Count, groupItems.ToList());
Removed where clause in the LINQ expression and implemented at InsertRange() method. This avoids redundant queries
var groupItems = MyList
.GroupBy(g => g.Id)
.Select(s => new
{ key = s.Key,
items = s.OrderBy(o => o.Item1)
.ThenBy(t => t.Item2)
}
)
.OrderBy(o => o.Id)
.SelectMany(sm => sm.Items).ToList();
MyList.Clear();
MyList.InsertRange(Mylist.Count, groupItems.Where(w => w.Code == 1));
MyList.InsertRange(Mylist.Count, groupItems.Where(w => w.Code == 2));

Categories

Resources