Forming a tournament table with LINQ (Fixture List) - c#

I have an array of players (string[]) and now I need to get an array of pairs representing games (playerN-playerM) to orginize tournament table like at this picture:
The desired end result is to generate a fixture list with all the games the need to be played.
How can I do this with LINQ in efficient way?
UPDATED:
A-B, A-C, A-D is not correct - games should be able to run in parallel.
I need result in the same order as at the picture

The following code can be used to generate a fixture list for a collection of teams to ensure that each time plays all other teams in 1 home and 1 away match.
The code is a bit long winded but it does work by providing you with a list in the order you have specified.
The code can probably be optimised but at the moment this is how it has come from my head.
NOTE: The resulting list will contain both Home and Away fixture, which based on your grid will be what you need to do anyway.
class Fixture
{
public string Home { get; set; }
public string Away { get; set; }
}
void CallCode()
{
string players = new string[] { "A", "B", "C", "D" };
List<Fixture> fixtures = CalculateFixtures(players);
}
List<Fixture> CalculateFixtures(string[] players)
{
//create a list of all possible fixtures (order not important)
List<Fixture> fixtures = new List<Fixture>();
for (int i = 0; i < players.Length; i++)
{
for (int j = 0; j < players.Length; j++)
{
if (players[i] != players[j])
{
fixtures.Add(new Fixture() { Home = players[i], Away = players[j] });
}
}
}
fixtures.Reverse();//reverse the fixture list as we are going to remove element from this and will therefore have to start at the end
//calculate the number of game weeks and the number of games per week
int gameweeks = (players.Length - 1) * 2;
int gamesPerWeek = gameweeks / 2;
List<Fixture> sortedFixtures = new List<Fixture>();
//foreach game week get all available fixture for that week and add to sorted list
for (int i = 0; i < gameweeks; i++)
{
sortedFixtures.AddRange(TakeUnique(fixtures, gamesPerWeek));
}
return sortedFixtures;
}
List<Fixture> TakeUnique(List<Fixture> fixtures, int gamesPerWeek)
{
List<Fixture> result = new List<Fixture>();
//pull enough fixture to cater for the number of game to play
for (int i = 0; i < gamesPerWeek; i++)
{
//loop all fixture to find an unused set of teams
for (int j = fixtures.Count - 1; j >= 0; j--)
{
//check to see if any teams in current fixtue have already been used this game week and ignore if they have
if (!result.Any(r => r.Home == fixtures[j].Home || r.Away == fixtures[j].Home || r.Home == fixtures[j].Away || r.Away == fixtures[j].Away))
{
//teams not yet used
result.Add(fixtures[j]);
fixtures.RemoveAt(j);
}
}
}
return result;
}

var games = players.SelectMany((player1, index) =>
players.Skip(index + 1).
Select(player2 => new {Player1 = player1, Player2 = player2}));
That should do it...

The implementation I really wanted:
public static List<List<Tuple<string, string>>> ListMatches(List<string> listTeam)
{
var result = new List<List<Tuple<string, string>>>();
int numDays = (listTeam.Count - 1);
int halfSize = listTeam.Count / 2;
var teams = new List<string>();
teams.AddRange(listTeam.Skip(halfSize).Take(halfSize));
teams.AddRange(listTeam.Skip(1).Take(halfSize - 1).ToArray().Reverse());
int teamsSize = teams.Count;
for (int day = 0; day < numDays; day++)
{
var round = new List<Tuple<string, string>>();
int teamIdx = day % teamsSize;
round.Add(new Tuple<string, string>(teams[teamIdx], listTeam[0]));
for (int idx = 1; idx < halfSize; idx++)
{
int firstTeam = (day + idx) % teamsSize;
int secondTeam = (day + teamsSize - idx) % teamsSize;
round.Add(new Tuple<string, string>(teams[firstTeam], teams[secondTeam]));
}
result.Add(round);
}
return result;
}

Related

Sort an object T-shirt by the size property with bubble sort

Been tackling this all day now and I am really fed up with it. I am new to sorting and I was working on bubble sort, quick sort and finally a bucket sort for my school exercise.
Here is what I have for bucket sort for a list of Objects(T-shirts) sorted by Cost. Now T-shirts Have also Size, Fabric and Color and that is why I need to sort them as well. I know this falls in the sort strings by bubble sort category but I can not find anything about it and everything I tried went bad
public List<Tshirt> BucketSort(Tshirt[] array)
{
List<Tshirt> result = new List<Tshirt>();
// Determine how many buckets you want to create
//Create buckets
int numOfBuckets = 5;
List<Tshirt>[] buckets = new List<Tshirt>[numOfBuckets];
for (int i = 0; i < 5; i++)
buckets[i] = new List<Tshirt>();
//Iterate through the passed array and add each tshirt to the appropriate bucket
for (int i = 0; i < array.Length; i++)
{
int buckitChoice = ((int)array[i].Cost / numOfBuckets);
buckets[buckitChoice].Add(array[i]);
}
//Sort each bucket and add it to the result List
//Each sublist is sorted using Bubblesort, but you could substitute any sorting algo you would like
for (int i = 0; i < numOfBuckets; i++)
{
Tshirt[] temp = BubbleSortList(buckets[i]);
result.AddRange(temp);
}
return result;
}
public static Tshirt[] BubbleSortList(List<Tshirt> input)
{
for (int i = 0; i < input.Count; i++)
{
for (int j = 0; j < input.Count; j++)
{
if (input[i].Cost < input[j].Cost)
{
decimal temp = input[i].Cost;
input[i].Cost = input[j].Cost;
input[j].Cost = temp;
}
}
}
return input.ToArray();
}
public Tshirt[] ReturnContexttoArray()
{
var tshirts = _context.Tshirts;
Tshirt[] TshirtArr = new Tshirt[tshirts.Count()];
var count = 0;
foreach (var tshirt in tshirts)
{
TshirtArr[count++] = tshirt;
}
return TshirtArr;
}
public List<Tshirt> ImplementBucketSortAsc()
{
return BucketSort(ReturnContexttoArray());
}

Simple Selection Sort won't sort

I'm new to algorithms and I tried to write a selection sort. With some help from the internet I have a script that should work, but doesn't. The result after the sort method is a list that is still unsorted.
I'm not sure if I missed anything and my code looks the same as the ones online.
Product.cs
public class Product
{
public string Name { get; set; }
public double Price { get; set; }
}
Order.cs
public class Order
{
public List<Product> listOfProducts = new List<Product>(){
new Product(){ Name="Item1", Price=2.55 },
new Product(){ Name="Item2", Price=1.92 },
new Product(){ Name="Item3", Price=2.12 }
};
public List<Product> GetAllProducts(){
return this.listOfProducts;
}
public void SortProductsByPrice(){
int min = 0;
for (int i = 0; i < this.listOfProducts.Count - 1; i++)
{
min = i;
for (int j = 0; j < this.listOfProducts.Count; j++)
{
if (listOfProducts[j].Price < listOfProducts[min].Price)
{
min = j;
}
}
Product temporary = listOfProducts[min];
listOfProducts[min] = listOfProducts[i];
listOfProducts[i] = temporary;
}
}
}
Program.cs
static void Main(string[] args)
{
Order order = new Order();
// unsorted list
foreach (Product pro in order.GetAllProducts())
{
Console.WriteLine(pro.Price);
}
Console.WriteLine("------------------------------------------");
order.SortProductsByPrice();
// sorted list
foreach (Product pro in order.GetAllProducts())
{
Console.WriteLine(pro.Price);
}
Console.ReadLine();
}
The problem in your code is in the nested loop.
If you take a closer look at the algorithm, you'll see that:
Selection sort is a simple sorting algorithm. This sorting algorithm is an in-place comparison-based algorithm in which the list is divided into two parts, the sorted part at the left end and the unsorted part at the right end. Initially, the sorted part is empty and the unsorted part is the entire list.
You're re-comparing your values with what you've already sorted, which you should not do. You're not getting a sorted list because by the end of your code, the values are being swapped over and over until they get back to their original order. A simple fix is by changing the nested for loop like this:
public void SortProductsByPrice()
{
int min = 0;
for (int i = 0; i < this.listOfProducts.Count - 1; i++)
{
min = i;
for (int j = i + 1; j < this.listOfProducts.Count; j++)
{
if (listOfProducts[j].Price < listOfProducts[min].Price)
{
min = j;
}
}
Product temporary = listOfProducts[min];
listOfProducts[min] = listOfProducts[i];
listOfProducts[i] = temporary;
}
}
So precisely, we just changed 1 line:
for (int j = i + 1; j < this.listOfProducts.Count; j++)
^^^^^
If you take another look at the pseudo-code in the above link, you'll see that this function now resembles it:
procedure selection sort
list : array of items
n : size of list
for i = 1 to n - 1
/* set current element as minimum*/
min = i
/* check the element to be minimum */
for j = i+1 to n
if list[j] < list[min] then
min = j;
end if
end for
/* swap the minimum element with the current element*/
if indexMin != i then
swap list[min] and list[i]
end if
end for
end procedure

Hackerrank: Climbing the Leaderboard

i have a deal with a hackerrank algorithm problem.
It works at all cases, except 6-7-8-9. It gives timeout error. I had spent so much time at this level. Someone saw where is problem?
static long[] climbingLeaderboard(long[] scores, long[] alice)
{
//long[] ranks = new long[scores.Length];
long[] aliceRanks = new long[alice.Length]; // same length with alice length
long lastPoint = 0;
long lastRank;
for (long i = 0; i < alice.Length; i++)
{
lastPoint = scores[0];
lastRank = 1;
bool isIn = false; // if never drop in if statement
for (long j = 0; j < scores.Length; j++)
{
if (lastPoint != scores[j]) //if score is not same, raise the variable
{
lastPoint = scores[j];
lastRank++;
}
if (alice[i] >= scores[j])
{
aliceRanks[i] = lastRank;
isIn = true;
break;
}
aliceRanks[i] = !isIn & j + 1 == scores.Length ? ++lastRank : aliceRanks[i]; //drop in here
}
}
return aliceRanks;
}
This problem can be solved in O(n) time, no binary search needed at all. First, we need to extract the most useful piece of data given in the problem statement, which is,
The existing leaderboard, scores, is in descending order.
Alice's scores, alice, are in ascending order.
An approach that makes this useful is to create two pointers, one at the start of alice array, let's call it "i", and the second is at the end of scores array, let's call it "j". We then loop until i reaches the end of alice array and at each iteration, we check for three main conditions. We increment i by one if alice[i] is less than scores[j] because the next element of alice may be also less than the current element of scores, or we decrement j if alice[i] is greater than scores[j] because we are sure that the next elements of alice are also greater than those elements discarded in scores. The last condition is that if alice[i] == scores[j], we only increment i.
I solved this question in C++, my goal here is to make you understand the algorithm, I think you can easily convert it to C# if you understand it. If there are any confusions, please tell me. Here is the code:
// Complete the climbingLeaderboard function below.
vector<int> climbingLeaderboard(vector<int> scores, vector<int> alice) {
int j = 1, i = 1;
// this is to remove duplicates from the scores vector
for(i =1; i < scores.size(); i++){
if(scores[i] != scores[i-1]){
scores[j++] = scores[i];
}
}
int size = scores.size();
for(i = 0; i < size-j; i++){
scores.pop_back();
}
vector<int> ranks;
i = 0;
j = scores.size()-1;
while(i < alice.size()){
if(j < 0){
ranks.push_back(1);
i++;
continue;
}
if(alice[i] < scores[j]){
ranks.push_back(j+2);
i++;
} else if(alice[i] > scores[j]){
j--;
} else {
ranks.push_back(j+1);
i++;
}
}
return ranks;
}
I think this may help you too:
vector is like an array list that resizes itself.
push_back() is inserting at the end of the vector.
pop_back() is removing from the end of the vector.
Here is my solution with c#
public static List<int> climbingLeaderboard(List<int> ranked, List<int> player)
{
List<int> result = new List<int>();
ranked = ranked.Distinct().ToList();
var pLength = player.Count;
var rLength = ranked.Count-1;
int j = rLength;
for (int i = 0; i < pLength; i++)
{
for (; j >= 0; j--)
{
if (player[i] == ranked[j])
{
result.Add(j + 1);
break;
}
else if(player[i] < ranked[j])
{
result.Add(j + 2);
break;
}
else if(player[i] > ranked[j]&&j==0)
{
result.Add(1);
break;
}enter code here
}
}
return result;
}
Here is a solution that utilizes BinarySearch. This method returns the index of the searched number in the array, or if the number is not found then it returns a negative number that is the bitwise complement of the index of the next element in the array. Binary search only works in sorted arrays.
public static int[] GetRanks(long[] scores, long[] person)
{
var defaultComparer = Comparer<long>.Default;
var reverseComparer = Comparer<long>.Create((x, y) => -defaultComparer.Compare(x, y));
var distinctOrderedScores = scores.Distinct().OrderBy(i => i, reverseComparer).ToArray();
return person
.Select(i => Array.BinarySearch(distinctOrderedScores, i, reverseComparer))
.Select(pos => (pos >= 0 ? pos : ~pos) + 1)
.ToArray();
}
Usage example:
var scores = new long[] { 100, 100, 50, 40, 40, 20, 10 };
var alice = new long[] { 5, 25, 50, 120 };
var ranks = GetRanks(scores, alice);
Console.WriteLine($"Ranks: {String.Join(", ", ranks)}");
Output:
Ranks: 6, 4, 2, 1
I was bored so i gave this a go with Linq and heavily commented it for you,
Given
public static IEnumerable<int> GetRanks(long[] scores, long[] person)
// Convert scores to a tuple
=> scores.Select(s => (scores: s, isPerson: false))
// convert persons score to a tuple and concat
.Concat(person.Select(s => (scores: s, isPerson: true)))
// Group by scores
.GroupBy(x => x.scores)
// order by score
.OrderBy(x => x.Key)
// select into an indexable tuple so we know everyones rank
.Select((groups, i) => (rank: i, groups))
// Filter the person
.Where(x => x.groups.Any(y => y.isPerson))
// select the rank
.Select(x => x.rank);
Usage
static void Main(string[] args)
{
var scores = new long[]{1, 34, 565, 43, 44, 56, 67};
var alice = new long[]{578, 40, 50, 67, 6};
var ranks = GetRanks(scores, alice);
foreach (var rank in ranks)
Console.WriteLine(rank);
}
Output
1
3
6
8
10
Based on the given constraint brute-force solution will not be efficient for the problem.
you have to optimize your code and the key part here is to look up for exact place which can be effectively done by using binary search.
Here is the solution using binary search:-
static int[] climbingLeaderboard(int[] scores, int[] alice) {
int n = scores.length;
int m = alice.length;
int res[] = new int[m];
int[] rank = new int[n];
rank[0] = 1;
for (int i = 1; i < n; i++) {
if (scores[i] == scores[i - 1]) {
rank[i] = rank[i - 1];
} else {
rank[i] = rank[i - 1] + 1;
}
}
for (int i = 0; i < m; i++) {
int aliceScore = alice[i];
if (aliceScore > scores[0]) {
res[i] = 1;
} else if (aliceScore < scores[n - 1]) {
res[i] = rank[n - 1] + 1;
} else {
int index = binarySearch(scores, aliceScore);
res[i] = rank[index];
}
}
return res;
}
private static int binarySearch(int[] a, int key) {
int lo = 0;
int hi = a.length - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (a[mid] == key) {
return mid;
} else if (a[mid] < key && key < a[mid - 1]) {
return mid;
} else if (a[mid] > key && key >= a[mid + 1]) {
return mid + 1;
} else if (a[mid] < key) {
hi = mid - 1;
} else if (a[mid] > key) {
lo = mid + 1;
}
}
return -1;
}
You can refer to this link for a more detailed video explanation.
static int[] climbingLeaderboard(int[] scores, int[] alice) {
int[] uniqueScores = IntStream.of(scores).distinct().toArray();
int [] rank = new int [alice.length];
int startIndex=0;
for(int j=alice.length-1; j>=0;j--) {
for(int i=startIndex; i<=uniqueScores.length-1;i++) {
if (alice[j]<uniqueScores[uniqueScores.length-1]){
rank[j]=uniqueScores.length+1;
break;
}
else if(alice[j]>=uniqueScores[i]) {
rank[j]=i+1;
startIndex=i;
break;
}
else{continue;}
}
}
return rank;
}
My solution in javascript for climbing the Leaderboard Hackerrank problem. The time complexity of the problem can be O(i+j), i is the length of scores and j is the length of alice. The space complexity is O(1).
// Complete the climbingLeaderboard function below.
function climbingLeaderboard(scores, alice) {
const ans = [];
let count = 0;
// the alice array is arranged in ascending order
let j = alice.length - 1;
for (let i = 0 ; i < scores.length ; i++) {
const score = scores[i];
for (; j >= 0 ; j--) {
if (alice[j] >= score) {
// if higher than score
ans.unshift(count+1);
} else if (i === scores.length - 1) {
// if smallest
ans.unshift(count+2);
} else {
break;
}
}
// actual rank of the score in leaderboard
if (score !== scores[i-1]) {
count++;
}
}
return ans;
}
Here is my solution
List<int> distinct = null;
List<int> rank = new List<int>();
foreach (int item in player)
{
ranked.Add(item);
ranked.Sort();
ranked.Reverse();
distinct = ranked.Distinct().ToList();
for (int i = 0; i < distinct.Count; i++)
{
if (item == distinct[i])
{
rank.Add(i + 1);
break;
}
}
}
return rank;
This can be modified by removing the inner for loop also
List<int> distinct = null;
List<int> rank = new List<int>();
foreach (int item in player)
{
ranked.Add(item);
ranked.Sort();
ranked.Reverse();
distinct = ranked.Distinct().ToList();
var index = ranked.FindIndex(x => x == item);
rank.Add(index + 1);
}
return rank;
This is my solution in c# for Hackerrank Climbing the Leaderboard based on C++ answer here.
public static List<int> climbingLeaderboard(List<int> ranked, List<int> player)
{
List<int> _ranked = new List<int>();
_ranked.Add(ranked[0]);
for(int a=1; a < ranked.Count(); a++)
if(_ranked[_ranked.Count()-1] != ranked[a])
_ranked.Add(ranked[a]);
int j = _ranked.Count()-1;
int i = 0;
while(i < player.Count())
{
if(j < 0)
{
player[i] = 1;
i++;
continue;
}
if(player[i] < _ranked[j])
{
player[i] = j+2;
i++;
}
else
if(player[i] == _ranked[j])
{
player[i] = j+1;
i++;
}
else
{
j--;
}
}
return player;
}
My solution in Java for climbing the Leaderboard Hackerrank problem.
// Complete the climbingLeaderboard function below.
static int[] climbingLeaderboard(int[] scores, int[] alice) {
Arrays.sort(scores);
HashSet<Integer> set = new HashSet<Integer>();
int[] ar = new int[alice.length];
int sc = 0;
for(int i=0; i<alice.length; i++){
sc = 1;
set.clear();
for(int j=0; j<scores.length; j++){
if(alice[i] < scores[j] && !set.contains(scores[j])){
sc++;
set.add(scores[j]);
}
}
ar[i] = sc;
}return ar;
}

Using For Loop and Foreach to create a list in C#

I am trying to create a draft order for a game I'm working on but I cannot get it to function how I need it to. I need 5 rounds with each round number properly and each pick should be numbered 1-10 for each round. Here is the code I'm working with:
List<Draft> _draftorder = new List<Draft>();
foreach (Team t in teams)
{
for (int i = 1; i <= 5; i++)
{
_draftorder.Add(new Draft()
{
city = t.city,
round = i++,
pick = i++,
});
}
}
Any help is appreciated!
I find the logic is easier to follow when the code matches the stepwise workflow of the requirements, and the variable names match the concepts in the requirements.
List<Draft> _draftorder = new List<Draft>();
for (int currentRound=1; currentRound<=10; currentRound++)
{
int pickOrder = 1;
foreach (Team t in teams)
{
_draftorder.Add(new Draft()
{
city = t.city,
round = currentRound,
pick = pickOrder++
});
}
}
You need to add an additional for loop and put the foreach loop inside the first for loop:
List<Draft> _draftorder = new List<Draft>();
// Allow 5 rounds
for (int i = 1; i <= 5; i++)
{
// Allow every team to pick some drafts
foreach (Team t in teams)
{
// Limit them to a specific amount of picks per round
for (int j = 1; j <= 10; j++)
{
_draftorder.Add(new Draft()
{
city = t.city,
round = i,
pick = j,
});
}
}
}

Process a list with a loop, taking 100 elements each time and automatically less than 100 at the end of the list

Is there a way to use a loop that takes the first 100 items in a big list, does something with them, then the next 100 etc but when it is nearing the end it automatically shortens the "100" step to the items remaining.
Currently I have to use two if loops:
for (int i = 0; i < listLength; i = i + 100)
{
if (i + 100 < listLength)
{
//Does its thing with a bigList.GetRange(i, 100)
}
else
{
//Does the same thing with bigList.GetRange(i, listLength - i)
}
}
Is there a better way of doing this? If not I will at least make the "thing" a function so the code does not have to be copied twice.
You can make use of LINQ Skip and Take and your code will be cleaner.
for (int i = 0; i < listLength; i=i+100)
{
var items = bigList.Skip(i).Take(100);
// Do something with 100 or remaining items
}
Note: If the items are less than 100 Take would give you the remaining ones.
I didn't like any of the answers listed, so I made my own extension:
public static class IEnumerableExtensions
{
public static IEnumerable<IEnumerable<T>> MakeGroupsOf<T>(this IEnumerable<T> source, int count)
{
var grouping = new List<T>();
foreach (var item in source)
{
grouping.Add(item);
if(grouping.Count == count)
{
yield return grouping;
grouping = new List<T>();
}
}
if (grouping.Count != 0)
{
yield return grouping;
}
}
}
Then you can use it:
foreach(var group in allItems.MakeGroupsOf(100))
{
// Do something
}
You can keep an explicit variable for the end point:
for (int i = 0, j; i < listLength; i = j)
{
j = Math.min(listLength, i + 100);
// do your thing with bigList.GetRange(i, j)
}
In dotnet 6 you can use chunk:
//Child collections will be comprised of 10 elements each.
IEnumerable<int[]> sublists = numbers.Chunk(10);
https://exceptionnotfound.net/bite-size-dotnet-6-chunk-in-linq/
There is also a reference to use a group by to do this, which is quite an interesting solution for older versions of the framework:
Split a collection into `n` parts with LINQ?
List<int> list = null;
int amount_of_hundreds = Math.Floor(list.Count/100);
int remaining_number = list.Count - (amount_of_hundreds * 100);
for(int i = 0; i < amount_of_hundreds; ++i)
{
for(int j = 0; j < 100; ++j)
{
int item = list[(i * 100) + j];
// do what you want with item
}
}
for(int i = 0; i < remaining_number; ++i)
{
int item = list[(amount_of_hundreds * 100) + i];
// do what you want with item
}
you can try below approach also:
int i = 1;
foreach (item x in bigList)
{
batchOperation.Insert(x); //replace it with your action; create the batch
i++;
if (i >100)
{
table.ExecuteBatch(batchOperation); //execute the batch
batchOperation.Clear();
i = 1; // re-initialize
}
}
if (batchOperation.Count >= 1 )
{
table.ExecuteBatch(batchOperation); //do this for the residue items
}
This is my solution, before all, i skip the last rows (rows - skiplast) after i get top of the array.
[TestMethod]
public void SkipTake()
{
var rows = 100;
var skip = 1;
var skiplast = 95;
var result = Enumerable.Range(1, rows).Take(rows - skiplast).Skip(skip - 1 == 0 ? 1 : skip).ToList();
}
You can split one big List to many small lists by limit, like this:
var limit = 100;
foreach (var smallList in bigList.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / limit)
.Select(x => x.Select(v => v.Value).ToList())
.ToList())
{
Console.WriteLine($"{smallList.Count}");
Console.WriteLine(String.Join("\r\n", smallList));
}

Categories

Resources