I am returning a list of items from the Database. List contains info like id, first name, last name, # of sales, $ revenue.
1 John James 431 213000
2 Scott Smith 301 43000
3 Jane Doe 431 300000
4 Tess Jones 431 14280
my results will contain the 4 rows as shown above. I am ordering my rows in descending order based on the # sales value. If the # sales value is the same then I order by id. So after ordering my results will look like so:
3 Jane Doe 431 300000
1 John James 431 213000
4 Tess Jones 431 14280
2 Scott Smith 301 43000
Now I would like to randomize the order of the rows where the #sales are the same. I am sure that means that I probably won't use the order by id.
How can I create a subarray of my results where the #sales are the same and then randomize their order and insert it to the original array? The main reason I am doing this is to add some variety to the data I will display so it not the same and gives the users the opportunity to be displayed in a different order.
This is how i am getting my original data:
results = results.OrderByDescending(x => Math.Max((uint)x.TotalSales, x.TotalRevenue))
.GroupBy(x => x.Id)
.Select(x => x.First()).ToList();
I believe I will be applying the randomizing part as follows:
var randomIndx= new Random(TotalSales).Next(100) % (#: will this be the number of duplicates?);
so that
I am not sure how to put things together. Any helpful tips are much appreciated.
Apply an ordering to your collection using Random, which will shuffle the results. Using ThenBy will ensure that entries are shuffled only within their previous-level ordering (items will be shuffled within their same "NumOfSales").
For example, with class:
public class Employee
{
public int Id{get;set;}
public string Name{get;set;}
public int NumOfSales{get;set;}
public int Revenue{get;set;}
}
Your code could look like this:
var originalEmployeeList = new List<Employee>()
{
new Employee { Id = 1, Name = "John James", NumOfSales = 431, Revenue = 213000 },
new Employee { Id = 2, Name = "Scott Smith", NumOfSales = 301, Revenue = 43000 },
new Employee { Id = 3, Name = "Jane Doe", NumOfSales = 431, Revenue = 300000 },
new Employee { Id = 4, Name = "Tess Jones", NumOfSales = 431, Revenue = 14280 },
};
var random = new Random();
var randomizedResults = originalEmployeeList
.OrderByDescending(x => x.NumOfSales)
.ThenBy(x => random.Next())
.ToList();
The key here is using random.Next() INSIDE a ThenBy. With this example, people with a NumOfSales = 431 will always appear before people with a NumOfSales = 301, but the listing of people within the same NumOfSales will be randomized.
Here's a runnable example: https://dotnetfiddle.net/W2ESGt
Related
I want to make an olympic medal ranking.
Right now I have a pivot table that has a CountryID, MatchID and Medal column.
The medal column stores the values 1 (gold), 2 (silver) and three (brass).
Countries with the most gold should be the heighest in the rank, followed by the most silver etc. Only thing is, when gold is equal then the amount of silver medals should be taken in account and ofcourse if that is also equal compare brass medals.
Right now I take the following (not successful) approach:
First I make 3 queries like this to get an overview of the amount of medals per country:
var resGold = context.olympics_landen_wedstrijd.Where(n => n.medal == 1).GroupBy(n => n.LandID).Select(g => new { g.Key, Count = g.Count(), Medal = 2 }).OrderByDescending(n => n.Count);
Then I combine resGold, resSilver and resBrass into one list. That list I query again to get a ranking where countries with the most gold are ranked higher.
var list = all.OrderBy(t => t.Medal == 1 ? 1 : (t.Medal == 2 ? 2 : t.Medal == 3 ? 3 : 4)).ToList();
The problem is that I end up with a list where countries are ranked more then once. When Germany and the USA both have 10 golden medals and they both also have silver medals then there is another record for both countries in the list.
How can I achieve to get a ranking according to olympic standards with my database setup?
The result I'm looking to create is:
[RANK 1] CountryID = 2, numberofGold = 10, numberofSilver = 3, numberOfBrass = 5
[RANK 2] CountryID = 3, numberofGold = 10, numberofSilver = 9, numberOfBrass = 9
[RANK 3] CountryID = 4, numberofGold = 9, numberofSilver = 10, numberOfBrass = 10
....
Thanks in advance!
You can compute score for every country based in Weighted Rank.
The ratio can be gold:silver:bronz = 5:3:1
you can sort country based on score Desc
Review
Olympic medal table
What I am trying to achieve is order the OrderSummary by highest Amount first and then display all other Order(s) in the collection for the given account one after another regardless of the Amount. Expected outcome is in the code snippet..
public class OrderSummary
{
public string FirstName { get; set; }
public decimal Amount { get; set; }
}
public class Worker
{
public List<OrderSummary> Orders { get; set; }
public Worker()
{
Orders = new List<OrderSummary>()
{
new OrderSummary() {FirstName = "James", Amount = 10.00m},
new OrderSummary() {FirstName = "Thomas", Amount = 11.00m},
new OrderSummary() {FirstName = "Leon", Amount = 13.00m},
new OrderSummary() {FirstName = "Lori", Amount = 14.00m},
new OrderSummary() {FirstName = "Thomas", Amount = 16.00m},
new OrderSummary() {FirstName = "Thomas", Amount = 6.00m},
new OrderSummary() {FirstName = "James", Amount = 19.00m}
};
}
//sorted by highest amount first
//then place firstname together regardless of the amount
//Expected Outcome
/*
James 19 -- highest amount followed by all other orders for James regardless of the amount.
* james 10
* thomas 16
* thomas 11
* thomas 6
* lori 14
* leon 13
*/
}
My approach was to get all elements that occurs more than once, then locate at which index the elements are except for the first one, remove it from the index, and add it to first element's index + 1.. Is there a better way to accomplish this?
GroupBy creates groups in the order that they appear in the source collection, so you can first order by amount, then group by name, then "flatten" the groups:
var results = Orders.OrderByDescending(o => o.Amount)
.GroupBy(o => o.FistName) // the groupings will be in order of the largest amount
.SelectMany(g => g); // flatten the groups
Try this:
var list = Orders.OrderByDescending(y => y.Amount).GroupBy(x => x.FirstName).Select(x=>x.Key).ToList();
This is ordering the records descending, then it is grouping them by FirstName, so you will have the one with the highest Amount first, then the others.
var maxAmount = Orders.Max(o => o.Amount);
var maxPos = Orders.IndexOf(maxAmount);
var maxOrder = Orders[maxPos];
Orders.RemoveAt(maxPos);
Orders.Insert(0, maxOrder);
EDIT
I didn't get that "one after another" would mean "grouped by name"...
Sorry, I have an example for you using .Net fiddle
Here it is
Answer To Question
Let me know if this helps
My approach was to get all elements that occurs more than once, then locate at which index the elements are except for the first one, remove it from the index, and add it to first element's index + 1
Here is the LINQ way of doing exactly what are you describing:
var query = from o in Orders
group o by o.FirstName into g
let top = g.Aggregate((a, b) => b.Amount > a.Amount ? b : a)
from o in Enumerable.Repeat(top, 1).Concat(g.Where(o => o != top))
select o;
You start by grouping the Orders by FirstName, then for each group you locate the element with the maximum Amount using Aggregate, put it first in a group and append the others. Finally flatten the result.
The data is as follow
ID Title Category About Link CategoryID
1 The Matrix Sci-Fi Text goes here http://... 1
2 The Simpsons Cartoon Text goes here http://... 2
3 Avengers Action Text goes here http://... 3
4 The Matrix Sci-Fi Text goes here http://... 1
5 The One Sci-Fi Text goes here http://... 1
6 The Hobbit Sci-Fi Text goes here http://... 1
I have a checkbox list containing the categories. The problem is if the user selects 'Action' and 'Sci-Fi' as category to display The Matrix will be displayed twice.
This is my try for getting unique rows in SQL Query.
select distinct title, about, link from mytable
inner join tableCategories on categoryID = tableCategoriesID
group by title, about, link
Using the LINQ,
(from table in movieTables
join x in categoryIDList
on categoryID equals x
slect table).Distinct()
Note that the categories are in a separate table linked by the categoryID.
Need help displaying unique or distinct rows in LINQ.
You can happily select your result into a list of whatever you want:
var v = from entry in tables
where matching_logic_here
select new {id = some_id, val=some_value};
and then you can run your distinct on that list (well, a ToList() on the above will make it one), based on your needs.
The following should illustrate what i mean (just paste into linqpad. if you're using VS, get rid of the .Dump():
void Main()
{
var input = new List<mock_entry> {
new mock_entry {id = 1, name="The Matrix", cat= "Sci-Fi"},
new mock_entry {id = 2, name="The Simpsons" ,cat= "Cartoon"},
new mock_entry {id = 3, name="Avengers" ,cat= "Action"},
new mock_entry {id = 4, name="The Matrix", cat= "Sci-Fi"},
new mock_entry {id = 5, name="The One" ,cat= "Sci-Fi"},
new mock_entry {id = 6, name="The Hobbit",cat= "Sci-Fi"},
};
var v = input.Where(e=>e.cat == "Action" || e.cat =="Sci-Fi")
.Dump()
.Select(e => new {n = e.name, c =e.cat})
.Dump()
;
var d = v.Distinct()
.Dump()
;
}
// Define other methods and classes here
public struct mock_entry {
public int id {get;set;}
public string name {get;set;}
public string cat {get;set;}
}
Another option would be to use DistinctBy from more linq as suggested in this question
Edit:
Even simpler, you can use GroupBy, and just select the first entry (you'll lose the id though, but up to you).
Here's an example that will work with the above:
var v = input.GroupBy (i => i.name)
.Select(e => e.First ())
.Dump()
.Where(e=>e.cat == "Action" || e.cat =="Sci-Fi")
.Dump()
;
will yield:
1 The Matrix Sci-Fi
3 Avengers Action
5 The One Sci-Fi
6 The Hobbit Sci-Fi
I have a generic list which needs to be filter based on another list (say, List<string>).
public class Model
{
public string ID { get; set;}
public string Make { get; set;}
}
List<Model> lstModel = new List<Model>();
And the lstModel is as follows
ID Make
---- -----------
5 MARUTI
4 BENZ
3 HYUNDAI
2 HONDA
1 TOYOTA
And i have another list which contains only car makers,ie
List<string> lstMakers = new List<string>() {"MARUTI", "HONDA"};
1) I need to filter lstModel which contains only items in lstMakers.
The output would be
ID Make
---- -----------
5 MARUTI
2 HONDA
2) Based on output (1), need another list of ids with 1 increment to each item in descending order,
The output would be List<int> ie,
6
5
3
2
Note: Using lambda expression / linq is more preferable
1 )
var list1 = lst.Where(x=>lstMakers.Contains(x.Make)).ToList();
2)
var list2 = list1.Select(x=>int.Parse(x.ID)+1)
.Concat(list1.Select(x=>int.Parse(x))
.OrderByDescending(x=>x)
.ToList();
Use Enumerable.Join and OrderByDescending:
var models = from maker in lstMakers
join model in lstModel
on maker equals model.Make
select model;
List<int> result = models
.Select(m => int.Parse(m.ID) + 1)
.OrderByDescending(i => i)
.ToList();
However, this selects two ints since only two models match. Your result contains 4 ints. I assume that your result is not related to your sample, is it?
but i need both the item and its incremental value,...
Now it's clear, use Enumerable.SelectMany with an array:
List<int> result = models
.Select(m => int.Parse(m.ID))
.SelectMany(id => new int[]{ id, id + 1 })
.OrderByDescending(id => id)
.Distinct()
.ToList();
I'm trying to group an object by one property (State). The end goal is to have a list. And within each list is an object consisting of two properties. I have data that looks like the following...
State Name ID
CA John 12
CA Kevin 13
CA Joe 14
AZ Sally 15754
AZ Stuart 1263
TN Sam 1211
How would one go about using linq to make this a list of object 'People' consisting of a person's name and ID grouped by state? This is what I've tried...but it's not working
var result = from groupOne in dataset
group groupOne by OneGroup.State into g
select new People() { People = g.ToList() }.ToList();
Why are you grouping by OneGroup.State, when OneGroup is not a part of the query?
That one should do the trick:
var result = from groupOne in dataset
group groupOne by groupOne.State into g
select new {
State = g.Key,
People = g.Select(x => new { x.Name, x.ID })
};
It returns anonymous types, but you can easily change it to return your class objects if you need.