How do I go about ranking a list based on one criteria, and then separating ties (of which there are likely to be a lot) based on another?
My (working) attempt to rank drivers based on their points totals.
for (int j = 0; j < career.NumberOfDrivers; j++)
{
int rank = 1;
for (int i = 0; i < career.NumberOfDrivers; i++)
{
if (career.driver[j].championshipPoints < career.driver[i].championshipPoints)
{
rank += 1;
}
}
career.driver[j].championshipRank = rank;
}
Presumably I then want to cycle through afterwards checking for ties - the problem is there are definitely going to be situations when I have several drivers tied on 0.
How do I go about simply breaking these tiebreakers alphabetically (ie based on first letter of driver's names)?
EDIT - my solution if its of interest. Pretty nasty hack in some respects but does the job.
List<Driver> tempDriver = new List<Driver>();
for (int i = 0; i < career.driver.Count; i++)
{
tempDriver.Add(career.driver[i]);
}
tempDriver.Sort(
delegate(Driver d1, Driver d2)
{
if (d1.championshipPoints == d2.championshipPoints)
{
return d1.Name.CompareTo(d2.Name);
}
return d1.championshipPoints.CompareTo(d2.championshipPoints);
}
);
for (int i = 0; i < career.NumberOfDrivers; i++)
{
for (int j = 0; j < career.NumberOfDrivers; j++)
{
if (career.driver[i].Name == tempDriver[j].Name)
{
career.driver[i].championshipRank = j + 1;
}
}
}
You can use Sort() on your drivers List, using a delegate.
If drivers have same points number, return the comparison result of their names.
Else return the comparison or their points
driversList.Sort(
delegate(Driver d1, Driver d2)
{
if (d1.championshipPoints == d2.championshipPoints)
{
return d1.name.CompareTo(d2.name);
}
return d1.championshipPoints.CompareTo(d2.championshipPoints);
}
);
Then the ranking of a driver is simply its index in the sorted list.
Edit:
This solution has n*log(n) complexity (the OP first attempt is O(n^2) )
Related
I was doing a coding test (for practice) that went something like this:
Input is a string with |'s and *'s.
E.g. **|*|*|**|***
Implement the function:
List<int> countItems(string line, List<int> startLocations, List<int> endLocations)
Count the number of * characters that are between an opening and closing pair of | characters.
Where the 2 locations are arrays with the start and end locations (indices) to consider withing the string line.
For example if line = *|*|* and startLocations = [1] and endLocations = [3] it means
I need to check the substring *|*.
And since there is only 1 pipe, the result is zero.
The location values seemed to be 1-based and not 0-based for some reason.
If the range was 1 and 5, for example, the result would be 1 because there is only 1 * between pipes.
The code I came up with that did manage to solve about half the test cases is as follows:
List<int> countItems(string line, List<int> startLocations, List<int> endLocations)
{
var results = new List<int>();
if (String.IsNullOrWhiteSpace(line) || startLocations.Count == 0)
{
return results;
}
for (var i = 0; i < startLocations.Count; i++)
{
var startIndex = startLocations[i] - 1;
var endIndex = endLocations[i] - 1;
var start = false;
var total = 0;
var tempTotal = 0;
for (var j = startIndex; j < endIndex; j++)
{
if (!start && line[j] == '|')
{
start = true;
tempTotal = 0;
}
else if (start && line[j] == '*')
{
tempTotal++;
}
else if (line[j] == '|')
{
total += tempTotal;
tempTotal = 0;
}
}
if (line[endIndex] == '|')
{
total += tempTotal;
}
results.Add(total);
}
return results;
}
All the test cases either passed or failed because it ran out of time.
The error said it exceeded a time of 3 seconds.
Now I couldn't see the actual data being passed into the tests, so I'm not able to test it more.
But I suspect the solution was some kind of temporary list or dictionary so as to only iterate over the string 1 time instead of many times as in my code.
I want to learn what kind of solution to use in cases like this, but not really sure if this is a common type of question where the solution has some kind of name or common concept.
I would appreciate any obvious pointers to solving this type of question or even links to similar programming challenges where I can practice more.
In this case I think the best option would be to use stack theory.
It is a variation of the parenthesis balancing problem. You can find more about it here
Article parenthesis balancing problem
I managed to redo the test and I found the answer and problem type.
This is a "plates between candles" type of problem.
I was trying to solve it myself but almost ran out of time and eventually just copy/pasted an answer I found.
This was a practice run, not an actual test or application.
This was the solution that worked... I'll be studying it to better understand it...
List<int> numberOfItems(string s, List<int> startIndices, List<int> endIndices)
{
int q = startIndices.Count;
int l = s.Length;
for (var i = 0; i < startIndices.Count; i++)
{
startIndices[i]--;
endIndices[i]--;
}
var ans = new List<int>();
var prev = new int[l];
var next = new int[l];
var preSum = new int[l];
int p = -1, nxt = -1;
// calculating prev candle up to this index.
for (int i = 0; i < l; i++)
{
if (s[i] == '|')
{
p = i;
}
prev[i] = p;
}
//Calculating next candle from this index.
for (int i = l - 1; i >= 0; i--)
{
if (s[i] == '|')
{
nxt = i;
}
next[i] = nxt;
}
int c = 0;
// calculating the number of stars between these indices.
for (int i = 0; i < l; i++)
{
if (s[i] == '*')
{
c++;
}
preSum[i] = c;
}
// calculating ans.
for (int k = 0; k < q; k++)
{
int i = startIndices[k];
int j = endIndices[k];
int right = prev[j];// position of left candle.
int left = next[i];// position of right candle.
//cout<<right<<left;
if (left == -1 || right == -1 || left > right)
{
ans.Add(0);
}
else
{
ans.Add(preSum[right] - preSum[left]);
}
}
return ans;
}
struct SSales
{
private int Y;
private double S;
public int Year
{
get { return Y; }
set { Y = value; }
}
public double Sale
{
get { return S; }
set { S = value; }
}
public SSales (int _year, double _sales)
{
Y = _year;
S = _sales;
}
private void Sortbutton_Click(object sender, EventArgs e)
{
listBox1.Items.Clear();
if (yearradio.Checked)
{
int temp = 0;
for (int i = 0; i < bubble.Length - 1; i++)
{
for (int j = 0; j < bubble.Length - 1; j++)
{
if (bubble[i + 1].Year < bubble[i].Year)
{
temp = bubble[i].Year;
bubble[i].Year = bubble[i + 1].Year;
bubble[i + 1].Year = temp;
}
}
}
}
if (salesradio.Checked)
{
double temp2 = 0;
for (int i = 0; i < bubble.Length - 1; i++)
{
for (int j = 0; j < bubble.Length - 1; j++)
{
if (bubble[i + 1].Sale > bubble[i].Sale)
{
temp2 = bubble[i].Sale;
bubble[i].Sale = bubble[i + 1].Sale;
bubble[i + 1].Sale = temp2;
}
}
}
}
for (int i = 0; i < bubble.Length; i++)
{
listBox1.Items.Add(bubble[i].ToString());
}
}
While my bubble sorting algorithms work perfectly fine, they only sort incrementally with each click of the Sort Button. I need the listbox to be completely sorted with 1 click.
Also, as my code is now, the Years and Sales reorganize completely independent of each other. When the Sales index changes, the corresponding Year index remains at the same location and vice versa.
I'm guessing a for loop with int j would work, but I'm not sure how to implement it. Any help would be appreciated!
I see two issues. One you are setting/exchanging the Properties of the struct, not the structs themselves. That is why your sales and years are out of sync. You need to exchange the entire struct. Something like:
var temp = bubble[i];
bubble[i] = bubble[i + 1];
bubble[i + 1] = temp;
This leads to the second issue. You have a double loop using index variables i and j. Your swap only uses i. If you are trying to do a bubble sort do you really need nested loops? Consider the pseudo-code implementation that can be found here bubble sort, you should quickly be able to see the issue. Model your sort after that example.
I will guess that you are doing bubble sort for learning / practice reasons. If not you should just use the inbuilt Array.Sort() or Enumerable.OrderBy() or something similar.
There are multiple things you have done wrong. I have the improved code below which I will explain
struct SSales {
public int Year { get; set; } // use auto-properties for brevity
public double Sale { get; set; } // use auto-properties for brevity
public SSales(int year, double sales) {
Year = year;
Sale = sales;
}
}
// Use a generic routine to Swap, instead of replicating the code multiple times
// Note that we are passing by reference so the actual array eventually gets sorted
// Also, don't swap the properties, but the whole record. Else it will corrupt your data
static void Swap<T>(ref T obj1, ref T obj2) {
var temp = obj1;
obj1 = obj2;
obj2 = temp;
}
// Write the sort routine separately. Sorts usually just need a way to compare records, which can be provided by Caller (IoC pattern)
static void Sort<T>(T[] items, Func<T, T, int> comparer) {
for (int i = 0; i < items.Length - 1; i++) {
// Every execution of the inner loop will bubble-up the largest element in the range
// Your array is getting sorted from the end, so you don't need to re-compare the already sorted part
for (int j = 0; j < items.Length - 1 - i; j++) {
if (comparer(items[j], items[j + 1]) > 0) // call the generic user provided comparer to know the sequence
Swap(ref items[j], ref items[j + 1]); // use teh generic swapper to swap elements in the array
}
}
}
private void Sortbutton_Click(object sender, EventArgs e) {
listBox1.Items.Clear();
if (yearradio.Checked) {
// Invoke the Sort routine, with the required comparer
Sort(bubble, (a, b) => a.Year - b.Year);
}
if (salesradio.Checked) {
// Invoke the Sort routine, with the required comparer
Sort(bubble, (a, b) => (int)(a.Sale - b.Sale));
}
for (int i = 0; i < bubble.Length; i++) {
listBox1.Items.Add(bubble[i].ToString());
}
}
Hope that clarifies the issues you are facing and also helps you learn how to write better C# code.
This question already has answers here:
Depth-first flattened collection of an object hierarchy using LINQ
(4 answers)
Closed 8 years ago.
I have the following object set up:
public class DrawingInstance
{
public string DrawingNum;
public string Rev;
public string Title;
public int LevelNum;
public string RefDesc;
public string DateRelease;
public string DrawingType;
public DrawingInstance ParentMember;
public int PageInstance;
public List<DrawingInstance> ChildMembers = new List<DrawingInstance>();
}
After gathering all of the data, I am currently accessing each child member one level at a time, like so:
for (int i = 0; i < drawingInstance.ChildMembers.Count; i++)
{
for (int j = 0; j < drawingInstance.ChildMembers[i].ChildMembers.Count; j++)
{
....
....
}
}
The number of levels in the file being processed can be different each time.
Is there a way through recursion to loop through and traverse an infinite number of levels? I need to process them 1 level at a time. So all of the i's will be processed, then all of the j's for each i are processed, and so on. Currently I have 10 blocks of code for processing a possible of 10 levels, but I feel like there has to be a better way to go about this.
EDIT
Thanks for the quick responses.
Here is a more detailed look straight from my code that gives a little more insight into how I am currently processing the objects
//Level 0 Pages
int _pageNum = PageNum;
int startIdx = 0;
int pageCount = 0;
pageCount = GetVisioPageCount(_treeArray.ChildMembers.Count);
for (int i = 0; i < pageCount; i++)
{
VisioSheetOutline tempSheet = new VisioSheetOutline();
tempSheet = GetSingleSheet(_treeArray, startIdx, _pageNum, (i + 1));
for (int cMember = 0; cMember < tempSheet.ChildPairs.Length; cMember++)
{
ParentDictionary.Add(tempSheet.ChildPairs[cMember].SingleInstance, tempSheet.SheetName);
}
SheetList.Add(tempSheet);
_pageNum++;
startIdx += 15;
}
//Level 1 Pages
for (int i = 0; i < _treeArray.ChildMembers.Count; i++)
{
pageCount = 0;
pageCount = GetVisioPageCount(_treeArray.ChildMembers[i].ChildMembers.Count);
startIdx = 0;
for (int j = 0; j < pageCount; j++)
{
VisioSheetOutline tempSheet = new VisioSheetOutline();
tempSheet = GetSingleSheet(_treeArray.ChildMembers[i], startIdx, _pageNum, (i + 1));
for (int cMember = 0; cMember < tempSheet.ChildPairs.Length; cMember++)
{
ParentDictionary.Add(tempSheet.ChildPairs[cMember].SingleInstance, tempSheet.SheetName);
}
SheetList.Add(tempSheet);
_pageNum++;
startIdx += 15;
}
}
//Level 2 Pages
for (int i = 0; i < _treeArray.ChildMembers.Count; i++)
{
for (int j = 0; j < _treeArray.ChildMembers[i].ChildMembers.Count; j++)
{
pageCount = 0;
pageCount = GetVisioPageCount(_treeArray.ChildMembers[i].ChildMembers[j].ChildMembers.Count);
startIdx = 0;
for (int k = 0; k < pageCount; k++)
{
VisioSheetOutline tempSheet = new VisioSheetOutline();
tempSheet = GetSingleSheet(_treeArray.ChildMembers[i].ChildMembers[j], startIdx, _pageNum, (i + 1));
for (int cMember = 0; cMember < tempSheet.ChildPairs.Length; cMember++)
{
ParentDictionary.Add(tempSheet.ChildPairs[cMember].SingleInstance, tempSheet.SheetName);
}
SheetList.Add(tempSheet);
_pageNum++;
startIdx += 15;
}
}
}
I am currently looking into a few of the suggestions that were made to see which one fits my particular need.
Yes, as you suggested, you can easily deal with this using recursion; you just need a recursive function:
public void ProcessDrawingData(DrawingInstance instance)
{
// Do processing
foreach (DrawingInstance d in ChildMembers)
ProcessDrawingData(d);
}
Call it with the parent instance. This won't do a true breadth-first traversal though, as the first child will execute its first childs children (first all the way down) and slowly unwind.
Microsoft's Ix-Main package contains a number of LINQ extensions, including the Expand method which will flatten a hierarchical layout:
IEnumerable<DrawingInstance> rootList = ...;
IEnumerable<DrawingInstance> flattened = rootList.Expand(x => x.ChildMembers);
You can use the foreach() statement. This will iterate through a group that you need, assuming the object implements IEnumerable.
In your case, try this:
foreach(DrawingInstance di in ChildMembers)
{
// Do something with di.
}
EDIT
If you need to do this repeatedly, you should have some sort of a recursive method that takes a DrawingInstance, like this:
public void RecursiveMethod(DrawingInstance d)
{
foreach(DrawingInstance di in d.ChildMembers)
{
RecursiveMethod(di);
}
}
I don't know your project, so it is up to you to figure out the base case, or if this recursive edit is what you actually want.
I'm trying to find an apartment number which is not already taken. So list<> is a list with already taken apartments and all<> is a list of all the apartments. so I try to iterate through them to find an apartment that is not already taken.
This does not work:
list<> // 4 indexes
all<> // 25 indexes
for (int i = 0;i < all.Count; i++)
{
for (int j = 0; j < list.Count; j++)
{
if (all[i].apartmentNr != list[j].apartmentNr)
{
apartmentNr = all[i].apartmentNr;
}
}
}
The problem is that you don't check all the list items so apartmentNr is set on first mismatch, but it's possibly taken on next list item. Thus you need to check all list items, before you can make a conclusion that it's free:
list<> // 4 indexes
all<> // 25 indexes
for (int i = 0;i < all.Count; i++)
{
bool taken = false;
for (int j = 0; j < list.Count; j++)
{
if (all[i].apartmentNr == list[j].apartmentNr)
{
taken = true;
break; // no need to check the rest
}
}
if (!taken) {
apartmentNr = all[i].apartmentNr;
break; // found first free
}
}
using System.Linq;
//availableApartments will contain all available apartments
var availableApartments = all.Except(list)
// Will get you the first item.
var freeApartment = availableApartments.FirstOrDefault()
Reference - Enumerable.Except
I have five arrays of varying lengths and I need to iterate through all of them generating all possible combinations of the contents. I'm currently using 5 nested for loops like so:
for (int a = 1; a < Array1.Length - 1; a++)
{
for (int b = 1; b < Array2.Length - 1; b++)
{
for (int c = 1; c < Array3.Length - 1; c++)
{
for (int d = 1; d < Array4.Length - 1; d++)
{
for (int e = 1; e < Array5.Length - 1; e++)
{
//do something
}
}
}
}
}
Due to the size of the arrays, I end up with more than 456 million iterations. I'm pretty new to programming in general, and C# specifically. I'm just curious if there is a more efficient way to accomplish this.
Thank you.
You go though that many iterations because there are that many combinations: this is called combinatorial explosion. You cannot do it more efficiently if you must go through all possible combinations.
You can code it with fewer lines of code or without hard-coding the number of arrays (five in your case) by using recursion. However, the number of iterations is not going to change, only the number of lines of code.
void processCombination(int[] combination) {
// combination[i] has the index of array #i
...
}
void combine(int p, int[] indexes, int[] sizes) {
if (p == indexes.Length) {
processCombination(indexes);
} else {
for (indexes[p] = 0 ; indexes[p] != sizes[p] ; indexes[p]++) {
combine(p+1, indexes, sizes);
}
}
}