Linq query group by weight - c#

AllWordIDsAndWeightings is a Dictionary<int, double> of unique ID's for words that appear in a search query, along with their corresponding weighting.
Given the following query:
returnedObjectIDs = new List<int>(db.WordObjectMaps
.Where(c =>
c.ForObjectTypeID == TopicObjectTypeID
&& AllWordIDsAndWeightings.Select(w=> w.Key).ToList().Contains(c.WordID)
)
.GroupBy(c => c.ForObjectID)
.Select(c => c.Key)
);
I'd like to modify it so that:
The group by statement contains the sum of word frequency * word weighting for each word. Then I can order by this value descending and take the top n records.
Is this possible?

returnedObjectIDs = new List<int>(db.WordObjectMaps
.Where(c =>
c.ForObjectTypeID == TopicObjectTypeID
&& AllWordIDsAndWeightings.ContainsKey(c.WordID)
)
.Select(c => new { Word = c, Weight = AllWordIDsAndWeightings[c.WordID])
.GroupBy(c => x.Word.Value * c.Weight) // replace with your condition
.Select(c => c.Key)
);

Related

Order by and group by and sum using SQL

What I am trying to do is get the top 10 most sold Vegetables by grouping them by an Id passed by parameter in a function and ordering them by the sum of their Quantity. I don't know how to use SUM or (total) quite yet but I thought I'd post it here seeking help. If you need me offering you anything else I will be ready.
This is my code:
TheVegLinQDataContext db = new TheVegLinQDataContext();
var query =db.OrderDetails.GroupBy(p => p.VegID)
.Select(g => g.OrderByDescending(p => p.Quantity)
.FirstOrDefault()).Take(10);
And this is an image of my database diagram
Group orders by Vegetable ID, then from each group select data you want and total quantity:
var query = db.OrderDetails
.GroupBy(od => od.VegID)
.Select(g => new {
VegID = g.Key,
Vegetable = g.First().Vegetable, // if you have navigation property
Total = g.Sum(od => od.Quantity)
})
.OrderByDescending(x => x.Total)
.Select(x => x.Vegetable) // remove if you want totals
.Take(10);
Since this is not clear that you are passing what type of id as function parameter, I'm assuming you are passing orderId as parameter.
First apply where conditions then group the result set after that order by Total sold Quantity then apply Take
LINQ query
var result = (from a in orderdetails
where a.OrderId == orderId //apply where condition as per your needs
group a by new { a.VegId } into group1
select new
{
group1.Key.VegId,
TotalQuantity = group1.Sum(x => x.Quantity),
group1.FirstOrDefault().Vegitable
}).OrderByDescending(a => a.TotalQuantity).Take(10);
Lamda (Method) Syntax
var result1 = orderdetails
//.Where(a => a.OrderId == 1) or just remove where if you don't need to filter
.GroupBy(x => x.VegId)
.Select(x => new
{
VegId = x.Key,
x.FirstOrDefault().Vegitable,
TotalQuantity = x.Sum(a => a.Quantity)
}).OrderByDescending(x => x.TotalQuantity).Take(10);

How to order groupBy items

Having this Linq query which returns grouping of 4 DateTime:
IEnumerable<IGrouping<DateTime, QuoteSnapshotModel>> lista = (from q in quoteModeList
where q.QuoteTradeType == "Q"
select q).GroupBy(n => n.ExceriseDate);
How can I order the groups by DateTime and get only the first group?
meaning **List<QuoteSnapshotModel>**
Also, how can I get only the second List<QuoteSnapshotModel> (according to DateTime)
Try this:
for this you have to create a list<QuoteSnapshotModel> within class QuoteSnapshotModel.
int record = 1;
List<QuoteSnapshotModel> result =
quoteModeList
.Where(x => x.QuoteTradeType == "Q")
.GroupBy(x => x.ExceriseDate,
(a, b) => new QuoteSnapshotModel
{
ExceriseDate = a,
ListQuoteSnapshotModel = b.ToList()
})
.OrderByDescending(t => t.ExceriseDate)
.Skip(record - 1).Take(1).ToList();
Update
You can use only the first group
List<QuoteSnapshotModel> list =quoteModeList.Where(x=>x.QuoteTradeType =="Q")
.GroupBy(x => x.ExceriseDate)
.OrderBy(x=>x.Key)
.FirstOrDefault().Select(x=>x.QuoteTradeType).ToList();
You can use only the second group
List<QuoteSnapshotModel> list =quoteModeList.Where(x=>x.QuoteTradeType =="Q")
.GroupBy(x => x.ExceriseDate)
.OrderBy(x=>x.Key)
.Skip(1).First().Select(x=>x.QuoteTradeType).ToList()
How can I order the groups by DateTime
.OrderBy(g => g.Key) or .OrderBy(g => g.First().ExerciseDate)
and get only the first group?
.First() (or .FirstOrDefault() if it's possible there are 0 groups)
how can I get only the second List (according to DateTime)
.Skip(1).Take(1) or .ElementAt(1) or .Skip(1).First()
Putting it all together:
IEnumerable<IGrouping<DateTime, QuoteSnapshotModel>> lista = (
from q in quoteModeList
where q.QuoteTradeType == "Q"
select q
).GroupBy(n => n.ExerciseDate);
IList<QuoteSnapshotModel> firstQuote = lista.OrderBy(x => x.Key).Select(x => x.ToList()).FirstOrDefault();
IList<QuoteSnapshotModel> secondQuote = lista.OrderBy(x => x.Key).Skip(1).Select(x => x.ToList()).FirstOrDefault();

Linq - Group By Id, Order By and Then select top 5 of each grouping

Is there some way in linq group By Id, Order By descending and then select top 5 of each grouping? Right now I have some code shown below, but I used .Take(5) and it obviously selects the top 5 regardless of grouping.
Items = list.GroupBy(x => x.Id)
.Select(x => x.OrderByDescending(y => y.Value))
.Select(y => new Home.SubModels.Item {
Name= y.FirstOrDefault().Name,
Value = y.FirstOrDefault().Value,
Id = y.FirstOrDefault().Id
})
You are almost there. Use Take in the Select statement:
var items = list.GroupBy(x => x.Id)
//For each IGrouping - order nested items and take 5 of them
.Select(x => x.OrderByDescending(y => y.Value).Take(5))
This will return an IEnumerable<IEnumerable<T>>. If you want it flattened replace Select with SelectMany

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();

How to filter List With LINQ C#

I need to filter a List<Students> into StudentsWitHighestDebts.
The criteria is that only students where ZachetDidNotPass has maximum value and maximum-1 in all List<Students> are included in the result.
var StudentsWitHighestDebts = students
.Where(s => s.ZachetDidNotPass.(some condition))
.OrderBy(s => s.Name)
.ToList();
For example, given a list of students that have ZachetDidNotPass values 0 1 2 5 6 7. The resulting StudentsWitHighestDebts should only contain the students with 7 and 6 values in ZachetDidNotPass.
First option: take 2 highest debts and filter students by ZachetDidNotPass:
var highestDebts = students.Select(s => s.ZachetDidNotPass)
.OrderByDescending(p => p).Take(2).ToArray();
var studentsWitHighestDebts = students
.Where(s => highestDebts.Contains(s.ZachetDidNotPass))
.OrderByDescending(s => s.ZachetDidNotPass).ToList();
Second option - group by ZachetDidNotPass, sort groups by key descending, take top 2 groups and select students from groups
var studentsWitHighestDebts = students.GroupBy(s => s.ZachetDidNotPass)
.OrderByDescending(g => g.Key).Take(2)
.SelectMany(g => g).ToList();
And third option (take students with highest debt and highestDebt - 1)
var highestDebt = students.Max(s => s.ZachetDidNotPass);
var studentsWitHighestDebts = students
.Where(s => s.ZachetDidNotPass == highestDebt || s.ZachetDidNotPass == highestDebt - 1)
.OrderByDescending(s => s.ZachetDidNotPass).ToList();

Categories