How to rank a list with multiple conditions on c# - c#

I am trying to figure out how to rank a list with multiple conditions:
This is my list:
Let me explain the list above:
a. 'AreaCode' is an unique ID for each group of the list.
b. I will rank the list based on highest value of Rank 1 (highlighted with yellow).
c. If Rank 1 has same value, I will find highest value of Rank 2 (highlighted by green).
d. If Rank 2 has same value, I will find highest value of Rank 3 (highlighted by blue).
e. In this case, there are same value for Rank 1 (7 and 7), so I will find the highest value for Rank 2, however the Rank 2 value also same (4 and 4), so I will find the highest value for Rank 3 (1 and 2).
f. So 'Ranking' column is the result that I want to achieve, which is a list with these value: [1, 1, 1, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 3, 3, 3].
Last time I can do but with simple scenario (there are no Rank 1, Rank 2 or Rank 3) using this code:
var rankings = myIndexList.GroupBy(x => x)
.OrderByDescending(g => g.Key)
.Select((g, i) => new { Key = g.Key, Rank = i + 1 })
.ToDictionary(x => x.Key, x => x.Rank);
var output = myIndexList.Select(x => new { Col1 = x, Rank = rankings[x] })
.ToList();
Any ideas to achieve the result on 'Ranking' column?
Thank You!

I recommend this.
public class Item
{
public string Name { get; set; }
public int Value { get; set; }
}
/////
var list = new List<(Item Col1, int Rank)>();
var nameGroup = items.GroupBy(i => i.Name);
var sorted = nameGroup.OrderBy(i => i.Key);
foreach (var group in sorted)
{
int ranking = 1;
foreach (var item in group.OrderByDescending(i => i.Value))
{
list.Add((Col1: item, Rank: ranking++));
}
}

Related

GroupBy - Get TotalCount

I have data like this
data
1 1 2 2 2 2 2 blank/ null 1 1 3 3 4 4 4 blank/ null blank/ null
Trying to get the base (total non blank rows) and percent column along with frequency.
So far Achieved to get this
Column Count
1 4
2 5
3 2
4 3
With this code
var result = data.GroupBy(x => x.Field<string>(xx), (row, l) => new Group { Column = row, Count = l.Count() })
I am trying groupby to get output something like this
Column Count Base Percent
1 4 14 28.57142857
2 5 14 35.71428571
3 2 14 14.28571429
4 3 14 21.42857143
Steps
Filter out unwanted entries (nulls)
Get the total count
Group your data
Project the data to your desired result
Like this
var data = new int?[] { 1, 1, 2, 2, 2, 2, 2, null, 1, 1, 3, 3, 4, 4, 4, null, null };
var filteredData = data.Where(x => x != null);
var totalCount = filteredData.Count();
var groups = filteredData.GroupBy(x => x);
var result = groups.Select(x => new { Value = x.Key, Count = x.Count(), Percent = x.Count() * 100.0 / totalCount });
Note: a group is a collection of the grouped items plus a key. The key is whatever your chose to group by. In this example, the key is an int. And because the group is a collection you can do stuff like .Count() on it.

Linq to select objects with unique Id and greatest date?

I have a list of objects that look like this:
public class A
{
public int Id {get; set;}
public DateTime Date {get;set;}
public int TypeId {get; set;}
public int Version {get; set;}
}
My data looks like this:
Id Date TypeId Version
1 10/3/18 1 1
2 10/3/18 1 2
3 10/4/18 1 1
4 10/4/18 2 1
How can I make a linq query to return these 2 in a list where it gets the item with the greatest date where the version # is 1 and also uses the TypeId to return more items?
Id Date TypeId Version
3 10/4/18 1 1
4 10/4/18 2 1
This is what I have tried but my code only returns one item because of my FirstOrDefault function.
var q = from n in A.All.Where(x => x.Version == 1)
group n by n.TypeId into g
select g.OrderByDescending(t => t.Date).FirstOrDefault();
You need to group by typeId and then in each group order elements by date. Then you can pick first element in each group. Try this code:
var input = new[]
{
new A {Id = 1, Date = new DateTime(2018, 10, 3), TypeId = 1, Version = 1},
new A {Id = 2, Date = new DateTime(2018, 10, 3), TypeId = 1, Version = 2},
new A {Id = 3, Date = new DateTime(2018, 10, 4), TypeId = 1, Version = 1},
new A {Id = 4, Date = new DateTime(2018, 10, 4), TypeId = 2, Version = 1},
};
var result = input.Where(a => a.Version == 1)
.GroupBy(a => a.TypeId)
.Select(g => g.OrderByDescending(x => x.Date).First())
.ToArray();

Sorting a List of Lists by the amount of intergers of the same type they contain

Hey everyone I have a list of lists which I am trying to sort based on how many of the same type of int it contains. Each item in the list of list contains 4 ints, for ex:
[0,1,2,3]
[0,1,1,1]
[0,2,2,2] etc. Im trying to sort the lists based on which contains the most of a specific int. So if I wanted to sort these three the [0,1,1,1] list would be on top since it contains three 1's etc. I was told to use linq to do this, but didnt know how to use linq.orderby with a method. Can someone help me if there is the correct approach? Thanks!
public class EnemyAI : MonoBehaviour
{
List<List<int>> listOfPaths = new List<List<int>>();
public void sortListofLists ()
{
listOfPaths.OrderBy(runeList => runeList.customMethod(runeType)); //rune type is an int
}
public int customMethod(int sort)
{
int holder = 0;
for (int i = 0; i < MethodList.Count; i++)
{
if (MethodList[i] == sort)
{
holder++;
}
}
return holder;
}
Just order descending by count of particular digits:
var list = new List<List<int>>()
{
new List<int> { 0, 1, 2, 3 },
new List<int> { 0, 1, 1, 1 },
new List<int> { 0, 2, 2, 2 }
};
var result = list.OrderByDescending(c => c.Count(y => y == 1)).ToList();
Your problem can be separated into two sub-problems.
1. How to get the count of the most frequent integer in List?
You can use the following LINQ query:
int countOfMostOccurences = arr.GroupBy(x => x).Max(x => x.Count());
2. How to sort a List by a certain rule in descending order:
list = list.SortByDescending(x => rule);
Now, combine it:
List<List<int>> lists = new List<List<int>>
{
new List<int> { 1, 2, 3, 4 },
new List<int> { 1, 3, 3, 3 },
new List<int> { 1, 2, 2, 3 },
new List<int> { 1, 1, 1, 1 }
};
lists = lists.OrderByDescending(x => x.GroupBy(g => g).Max(g => g.Count())).ToList();

List unique transformation in LINQ

I have an ordered list of entities. Each entity has an int UniqueKey property.
I want the list to go through a transformation whereby the UniqueKey values are made unique (assuming that there are duplicates). This is done by finding duplicates and progressively incrementing them.
Step-by-step Process:
Start at index 1 (I am using zero-based indexing)
If any previous elements have the same UniqueId value, increment the value at the current index.
Repeat (2) until no previous elements have the same UniqueId
Move one element to the right
For example, { 1, 1, 1, 3, 3, 8 } would go through the following steps:
{ 1, 2, 1, 3, 3, 8 } : Index 1 incremented
{ 1, 2, 2, 3, 3, 8 } : Index 2 incremented
{ 1, 2, 3, 3, 3, 8 } : Index 2 incremented again
{ 1, 2, 3, 4, 3, 8 } : Index 3 incremented
{ 1, 2, 3, 4, 4, 8 } : Index 4 incremented
{ 1, 2, 3, 4, 5, 8 } : Index 4 incremented again
The code below performs the above-mentioned algorithm in a very procedural manner:
entities = entities.OrderBy(x => x.UniqueId);
foreach (var entity in entities)
{
var leftList = entities.Take(entities.IndexOf(entity));
while (leftList.Any(x => x.UniqueId == entity.UniqueId))
{
entity.UniqueId++;
}
}
Question: Is it possible to implement this in LINQ?
You algorithm could be simplify a lot. Just iterate over, and if an Id is lower than the previous, increment it by one. No Linq, no O(n^2), just O(n):
{ 1, 2, 1, 3, 3, 8 } : Index 1 incremented
{ 1, 2, 3, 3, 3, 8 } : Index 2 incremented
{ 1, 2, 3, 4, 3, 8 } : Index 3 incremented
{ 1, 2, 3, 4, 5, 8 } : Index 4 incremented
entities = entities.OrderBy(x => x.UniqueId).ToList();
for(int index = 1; index < entities.Count; index++)
{
int previous = entities[index - 1].UniqueId;
if (previous >= entities[index].UniqueId)
{
entities[index].UniqueId = previous + 1;
}
}
Technically yes:
var indexedEntities =
entities.Select((e, i) => new { Entity = e, Index = i })
.ToList();
indexedEntities.ForEach(ie =>
ie.Entity.UniqueId =
indexedEntities.Any(prev => prev.Index < ie.Index)
&& ie.Entity.UniqueId
<= indexedEntities.TakeWhile(prev => prev.Index < ie.Index)
.Max(prev => prev.Entity.UniqueId)
? indexedEntities.TakeWhile(prev => prev.Index < ie.Index)
.Max(prev => prev.Entity.UniqueId) + 1
: ie.Entity.UniqueId);
var result = indexedEntities.Select(ie => ie.Entity);
Though please, for the love of everything sacred in IT, don't, just don't :)
This doesn't follow your algorithm faithfully, but it might give you the result you want.
Essentially compare each element with the next, and increment the latter's Id to be one more than the former's.
entities.OrderBy(e => e.Id)
.Aggregate((e1, e2) => { if (e1.Id >= e2.Id) { e2.Id = e1.Id + 1; } return e2; });
If you are desperate for a linq solution why not just use the index as the id.
entities.OrderBy(x => x.UniqueId).Select((x,i) => {
x.UniqueId = i;
return x;
}).ToArray();

How to return duplicate values from an array?

Is there a way that I could return duplicate values from an array in C#? also im looking to write a small algorithm that would return the most number of duplicate values in an array. for example
[1, 2,2,2 3,3] I need to return the duplicate values with the most number of occurrences and the number of occurrences as well.
I think I saw some post which said that It could be done using Linq but I have no clue what Linq is
Any help would be much appreciated.
Try this:
int[] data = new int[] { 1, 2, 2, 2, 3, 3 };
IGrouping<int, int> mostOccurrences = data
.GroupBy(value => value)
.OrderByDescending(group => group.Count())
.First();
Console.WriteLine("Value {0} occurred {1} time(s).", mostOccurrences.Key, mostOccurrences.Count());
Note that if multiple values occur the same number of times (such as if you added another 3 to that list), the above code will only list one of them. To handle that situation, try this:
int[] data = new int[] { 1, 2, 2, 2, 3, 3, 3 };
var occurrenceInfos = data
.GroupBy(value => value)
.Select(group =>
new {
Count = group.Count(),
Value = group.Key
}
);
int maxOccurrenceCount = occurrenceInfos.Max(info => info.Count);
IEnumerable<int> maxOccurrenceValues = occurrenceInfos
.Where(info => info.Count == maxOccurrenceCount)
.Select(info => info.Value);
foreach (int value in maxOccurrenceValues)
Console.WriteLine("Value {0} occurred {1} time(s).", value, maxOccurrenceCount);
Here's my take on this:
var data = new[] { 1, 2, 2, 2, 3, 3, };
var occurences =
data
.ToLookup(x => x)
.ToDictionary(x => x.Key, x => x.Count());
var mostOccurences =
occurences
.OrderByDescending(x => x.Value)
.First();
These will give you the following results:

Categories