Get product of items in list C# linq - c#

I have a list with people with integerIds who share Tokens I am trying to get the products of each pair of friends id numbers. so friend id 2 and 4 would result in 8 etc.
var FriendsWhoShareTheMostTokens = FriendsWithTheirSharedTokensAmount.Select(x => new
{
FriendIdA = x.FriendTo.FriendID,
FriendIdB = x.FriendFor.FriendID,
SharedCountNumber = x.SharedCount
})
.Where(x => x.SharedCountNumber == FriendsWithTheirSharedTokensAmount.Max(y => y.SharedCount))
.ToList();
// need some code here**
foreach (var pairOffriendsWhoSharetheMostTokens in FriendsWhoShareTheMostTokens)
{
}
Can I accomplish this with Linq or what is the best way to accomplish this?

Edit
The answer is simple
var ProductofGreatestTokenSharers = FriendsWhoShareTheMostTokens.Select(x => new
{
ProductOfIds = x.FriendIdA * x.FriendIdB
}
);

It's really hard to understand what you are trying to achieve with your example.
However, if you want to have the id's and the product, and also obtain the highest SharedCountNumber.
You can probably just do the following
var someResult = someList.Select(x => new
{
FriendIdA = x.FriendTo.FriendID,
FriendIdB = x.FriendFor.FriendID,
SharedCountNumber = x.FriendTo.FriendID * x.FriendFor.FriendID
}) // Projection
.OrderByDescending(x => x.SharedCountNumber) // order the list
.FirstOrDefault(); // get the first
if (someResult != null)
{
Debug.WriteLine($"A : {someResult.FriendIdA}, B : {someResult.FriendIdB}, Product : {someResult.SharedCountNumber}");
}

Related

How can I get the best seller by product group using LINQ

I need to retrieve the name, product type and sum of the seller with the highest sales in each product type using linq and return the list to a view.
{
public ActionResult Query2()
{
// this part gets me everything into one model type
List<QueryViewModel> result = db.SaleProducts
.GroupBy(prod => prod.Type)
.SelectMany(sale => sale.Select(
row => new QueryViewModel
{
Seller = row.Sale.User.Name,
ProductType = row.Type,
Sales = (double)sale.Where(x => x.Sale.UserId == row.Sale.UserId && x.Type.Equals(row.Type)).Sum(price => price.Price)
}
)).Distinct().ToList<QueryViewModel>();
// this gives me the best per product type but i cant get the seller name
List<QueryViewModel> filter = (from res in result
group res by res.ProductType into prodGroup
select new QueryViewModel
{
ProductType = prodGroup.Key,
Sales = prodGroup.Max(x => x.Sales)
}).ToList<QueryViewModel>();
// this is really just to get the seller name at this point
List<QueryViewModel> something = (from res in result
join f in filter on res.Sales equals f.Sales
select new QueryViewModel
{
Seller = res.Seller,
ProductType = res.ProductType,
Sales = res.Sales
}).ToList<QueryViewModel>();
return View(something);
}
}
It should return a list of QueryViewModel(name, product type, total sales).
It does return that, but this seems horribly messy and I'm not understanding LINQ enough to clear this up.
Is there a better cleaner way to achieve my desired output?
The first grouping should be by type and Seller.
Something like this should get you going:
Quote_Masters.GroupBy(qm => new { qm.OrderTypeId, qm.SalesRepEmployeeId })
.Select(x => new { Name = x.Key.SalesRepEmployeeId, x.Key.OrderTypeId, Total = x.Sum(qm => qm.Total_Price) })
.OrderByDescending(x => x.Total).GroupBy(x => x.OrderTypeId).Select(x => x.FirstOrDefault())
Just change out your appropriate tables and fields.
Edit: Your select to QueryViewModel would be the new {Name = x.Key.SalesRepEmployeeId, x.Key.OrderTypeId, Total = x.Sum(qm => qm.Total_Price)}

Finding the most specific matching item

User input will be like 'BY1 2PX', which will split and stored into list like below
var items = new List<string> {'BY1 2PX', 'BY12', 'BY1', 'BY'};
I have source list of Products
public class Product
{
public string Name {get;set;}
public string Id {get;set;}
}
Below is a sample product list. There is no guarentee on ordering, it could be in any order.
var products = new List<Product>{
new Product("1", "BY1 2PX"),
new Product("2", "BY12"),
new Product("3", "BY1"),
new Product("4", "BY"),
new Product("5", "AA2 B2X"),
//...etc
}
my output should fetch 1, because its most specific match. If Id = 1 is not there then it should have fetched Id =2 like that...etc Could anyone help me in writing a linq query. I have tried something like below, is this fine?
var result = items.Select(x => products.FirstOrDefault(p =>
string.Equals(p.Name.Trim(), x, StringComparison.OrdinalIgnoreCase)))
.FirstOrDefault();
Well, you can use dictionary with its fast lookups :
var productsDict = products.ToDictionary(p => p.Name, p => p);
var key = items.FirstOrDefault(i => productsDict.ContainsKey(i));
Product result = key != null ? productsDict[key] : null;
Or as Tim suggested, if you have multiple elements with same names you can use Lookup :
var productsDict = products.ToLookup(p => p.Name, p => p);
var key = items.FirstOrDefault(i => productsDict.Contains(i));
Product result = key != null ? productsDict[key] : null;
If you want to select the best-matching product you need to select from the product- not the string-list. You could use following LINQ approach that uses List.FindIndex:
Product bestProduct = products
.Select(p => new {
Product = p,
Index = items.FindIndex(s => String.Equals(p.Name, s, StringComparison.OrdinalIgnoreCase))
})
.Where(x => x.Index != -1)
.OrderBy(x => x.Index) // ensures the best-match logic
.Select(x => x.Product)
.FirstOrDefault();
The Where ensures that you won't get an arbitrary product if there is no matching one.
Update:
A more efficient solution is this query:
Product bestProduct = items
.Select(item => products.FirstOrDefault(p => String.Equals(p.Name, item, StringComparison.OrdinalIgnoreCase)))
.FirstOrDefault(p != null); // ensures the best-match logic
You can try to find resemblance of words by using a specific algorythm called Levenshtein's distance algorythm, which is mostly used on "Did you mean 'word'" on most search websites.
This solution can be found here:
https://stackoverflow.com/a/9453762/1372750
Once you find the distance difference, you can measure which word or phrase is more "like" the searched one.
This will find for each product what is the "most specific" (the longest) match in items and will return the product with the longest match (regardless to order of either of the collections)
var result = products
.Select(p => new
{
Product = p,
MostSpecific = items.Where(item => p.Name.Contains(item))
.OrderByDescending(match => match.Length
.FirstOrDefault()
})
.Where(x => x.MostSpecific != null)
.OrderByDescending(x => x.MostSpecific.Length)
.Select(x => x.Product)
.FirstOrDefault();

having trouble with groupby and sum

Good evening guys. I'm currently developing a web application with the use of asp.net mvc and C# and I'm having trouble with using the groupby() and sum(). I'm attaching a screenshot of the output to better understand the situation i'm in.
what I want to do here is to group all with the same description and the total is to be summed. I have tried groupby and sum but i cant seem to arrive at the desired output. Can anyone please give me some good points on how to do this?
here is the codes i have tried in the manager:
public IEnumerable<TuitionFeeRevenuePerProgramVM_L> getDetails(string orgUnit, short? academicYear, byte? academicPeriod)
{
var depts = getAllOrgUnitDepByOrgUnitSchID(orgUnit);
var revenue = tuitionFee.SearchFor(x => x.AcademicPeriod == academicPeriod && x.AcademicYear == academicYear && depts.Contains(x.Course.OrgUnitCode) && x.IsLatest == true, "AssessmentItem.ChartOfAccount,AssessmentItem.ChartOfAccount.CostCenter,Course,Course.CourseSchedule,Course.OrgUnit,Course.OrgUnit.OrgUnitHierarchy1,Course.OrgUnit.OrgUnitHierarchy1.OrgUnit").ToList();
var r = revenue.GroupBy(x => x.Course.OrgUnitCode).Select(x => x.First()).ToList(); //this line seems to group them but the output is the first total amount not the overall total
var rev = revenue.Select(x => new TuitionFeeRevenuePerProgramVM_L
{
CostCenterCode = x.AssessmentItem.ChartOfAccount.CostCenterID,
CostCenterDesc = x.Course.OrgUnit.OrgUnitDesc,
Subjects = getNumberOfSubjects(x.Course.OrgUnitCode, x.AcademicYear, x.AcademicPeriod),
Enrolees = revenue.Where(y => y.Course.OrgUnitCode == x.Course.OrgUnitCode).Select(z => z.Course.CourseSchedule.Sum(a => a.EnrolledStudents)).Sum(b => b.Value),//(int)x.Course.CourseSchedule.Sum(y => y.EnrolledStudents),
Total = (decimal)((x.Course.CourseSchedule.Sum(y => y.EnrolledStudents) * x.Amount) * x.Course.ContactHrs) /*(revenue.Where(y => y.Course.OrgUnitCode == x.Course.OrgUnitCode).Select(z => z.Course.CourseSchedule.Sum(a => a.EnrolledStudents)).Sum(b => b.Value) * revenue.Where(d => d.Course.OrgUnitCode == x.Course.OrgUnitCode).Sum(e=>e.Amount)) * revenue.Where(f => f.Course.OrgUnitCode == x.Course.OrgUnitCode).Sum(g=>g.Course.ContactHrs) //*/
});
return rev.ToList();
}
the controller,model and view is as is since all the logic involved will be placed in the manager. I have tried to use Sum() when returning but it gets an error since the controller receives a list and not a decimal.
from d in data
group d by d.Description into g
from gg in g
select new {Description = gg.Key, Sum = gg.Sum(ggg=>ggg.TotalCost)}
Something like that without seeing the data class

Can this query about finding missing keys be improved? (either SQL or LINQ)

I am developing a ASP.NET MVC website and is looking a way to improve this routine. It can be improved either at LINQ level or SQL Server level. I hope at best we can do it within one query call.
Here is the tables involved and some example data:
We have no constraint that every Key has to have each LanguageId value, and indeed the business logic does not allow such contraint. However, at application level, we want to warn the admin that a key is missing a/some language values. So I have this class and query:
public class LocalizationKeyWithMissingCodes
{
public string Key { get; set; }
public IEnumerable<string> MissingCodes { get; set; }
}
This method get the Key list, as well as any missing codes (for example, if we have en + jp + ch language codes, and the key only has values for en + ch, the list will contains jp):
public IEnumerable<LocalizationKeyWithMissingCodes> GetAllKeysWithMissingCodes()
{
var languageList = Utils.ResolveDependency<ILanguageRepository>().GetActive();
var languageIdList = languageList.Select(q => q.Id);
var languageIdDictionary = languageList.ToDictionary(q => q.Id);
var keyList = this.GetActive()
.Select(q => q.Key)
.Distinct();
var result = new List<LocalizationKeyWithMissingCodes>();
foreach (var key in keyList)
{
// Get missing codes
var existingCodes = this.Get(q => q.Active && q.Key == key)
.Select(q => q.LanguageId);
// ToList to make sure it is processed at application
var missingLangId = languageList.Where(q => !existingCodes.Contains(q.Id))
.ToList();
result.Add(new LocalizationKeyWithMissingCodes()
{
Key = key,
MissingCodes = missingLangId
.Select(q => languageIdDictionary[q.Id].Code),
});
}
result = result.OrderByDescending(q => q.MissingCodes.Count() > 0)
.ThenBy(q => q.Key)
.ToList();
return result;
}
I think my current solution is not good, because it make a query call for each key. Is there a way to improve it, by either making it faster, or pack within one query call?
EDIT: This is the final query of the answer:
public IQueryable<LocalizationKeyWithMissingCodes> GetAllKeysWithMissingCodes()
{
var languageList = Utils.ResolveDependency<ILanguageRepository>().GetActive();
var localizationList = this.GetActive();
return localizationList
.GroupBy(q => q.Key, (key, items) => new LocalizationKeyWithMissingCodes()
{
Key = key,
MissingCodes = languageList
.GroupJoin(
items,
lang => lang.Id,
loc => loc.LanguageId,
(lang, loc) => loc.Any() ? null : lang)
.Where(q => q != null)
.Select(q => q.Code)
}).OrderByDescending(q => q.MissingCodes.Count() > 0) // Show the missing keys on the top
.ThenBy(q => q.Key);
}
Another possibility, using LINQ:
public IEnumerable<LocalizationKeyWithMissingCodes> GetAllKeysWithMissingCodes(
List<Language> languages,
List<Localization> localizations)
{
return localizations
.GroupBy(x => x.Key, (key, items) => new LocalizationKeyWithMissingCodes
{
Key = key,
MissingCodes = languages
.GroupJoin( // check if there is one or more match for each language
items,
x => x.Id,
y => y.LanguageId,
(x, ys) => ys.Any() ? null : x)
.Where(x => x != null) // eliminate all languages with a match
.Select(x => x.Code) // grab the code
})
.Where(x => x.MissingCodes.Any()); // eliminate all complete keys
}
Here is the SQL logic to identify the keys that are missing "complete" language assignments:
SELECT
all.[Key],
all.LanguageId
FROM
(
SELECT
loc.[Key],
lang.LanguageId
FROM
Language lang
FULL OUTER JOIN
Localization loc
ON (1 = 1)
WHERE
lang.Active = 1
) all
LEFT JOIN
Localization loc
ON (loc.[Key] = all.[Key])
AND (loc.LanguageId = all.LanguageId)
WHERE
loc.[Key] IS NULL;
To see all keys (instead of filtering):
SELECT
all.[Key],
all.LanguageId,
CASE WHEN loc.[Key] IS NULL THEN 1 ELSE 0 END AS Flagged
FROM
(
SELECT
loc.[Key],
lang.LanguageId
FROM
Language lang
FULL OUTER JOIN
Localization loc
ON (1 = 1)
WHERE
lang.Active = 1
) all
LEFT JOIN
Localization loc
ON (loc.[Key] = all.[Key])
AND (loc.LanguageId = all.LanguageId);
your code seems to be doing a lot of database query and materialization..
in terms of LINQ, the single query would look like this..
we take the cartesian product of language and localization tables to get all combinations of (key, code) and then subtract the (key, code) tuples that exist in the relationship. this gives us the (key, code) combination that don't exist.
var result = context.Languages.Join(context.Localizations, lang => true,
loc => true, (lang, loc) => new { Key = loc.Key, Code = lang.Code })
.Except(context.Languages.Join(context.Localizations, lang => lang.Id,
loc => loc.LanguageId, (lang, loc) => new { Key = loc.Key, Code = lang.Code }))
.GroupBy(r => r.Key).Select(r => new LocalizationKeyWithMissingCodes
{
Key = r.Key,
MissingCodes = r.Select(kc => kc.Code).ToList()
})
.ToList()
.OrderByDescending(lkmc => lkmc.MissingCodes.Count())
.ThenBy(lkmc => lkmc.Key).ToList();
p.s. i typed this LINQ query on the go, so let me know if it has syntax issues..
the gist of the query is that we take a cartesian product and subtract matching rows.

Simple LINQ Order by not working

I'm new with LINQ. I'm using this function:
public IEnumerable<Vendedores> GetVendedores()
{
using (var context = new OhmioEntities())
{
Vendedores _allvendors= new Vendedores();
_allvendors.Nombre = "All Vendors";
_allvendors.ID_Vendedor = -1;
var query = context.Vendedores;
var _vendors= query.Where(f => f.Activo == true).OrderBy(o=>Nombre).ToList();
_vendors.Insert(0, _allvendors);
return _vendors;
}
}
It should give me order list of active vendors. The where part work fine, but the order is ignored and the records after the .ToList are in the original table order. What i'm i doing wrong?
Thank you!
I think you need o.Nombre instead of Nombre
var _vendors = query
.Where(f => f.Activo)
.OrderBy(o=> o.Nombre)
.ToList();
Also f => f.Activo == true can be written as f => f.Activo.
it should be this way:
var _vendors= query.Where(f => f.Activo == true).OrderBy(o=>o.Nombre).ToList();

Categories