I wrote a struct
public struct SeasonEpisodeNr
{
public int seasonNr;
public int episodeNr;
}
During my program I will add those structs to an ArrayList. How can I sort them? I tried the IComparer but unfortunately I was not able to understand how it works.
I didn't test this but it's something like...
public struct SeasonEpisodeNr: IComparable
{
public int seasonNr;
public int episodeNr;
public int CompareTo(Object Item)
{
SeasonEpisodeNr that = (SeasonEpisodeNr) Item;
if (this.seasonNr > that.seasonNr)
return -1;
if (this.seasonNr < that.seasonNr)
return 1;
if (this.episodeNr > that.episodeNr)
return -1;
if (this.episodeNr < that.episodeNr)
return 1;
return 0;
}
public struct SeasonEpisodeNr
{
public SeasonEpisodeNr(int seasonNr, int episodeNr)
{
this.seasonNr = seasonNr;
this.episodeNr = episodeNr;
}
public int seasonNr; public int episodeNr;
}
static void Main(string[] args)
{
List<SeasonEpisodeNr> list = new List<SeasonEpisodeNr>();
list.Add(new SeasonEpisodeNr(1, 2));
list.Add(new SeasonEpisodeNr(1, 1));
list.Sort((a, b) =>
{
//implement comparison, e.g. compare season first and if equal compare the epizods
int res = a.seasonNr.CompareTo(b.seasonNr);
return res != 0 ? res : a.episodeNr.CompareTo(b.episodeNr);
});
}
Check out the example in this link
http://msdn.microsoft.com/en-us/library/8ehhxeaf.aspx
The basic idea is to
Create a IComparer implementation that returns -1 (less than), 0 (equals) or 1 (greater than) based on your custom comparison criteria.
Next pass an instance of this class to the Sort method of your List()
Another (a bit long-drawn) example that illustrates this
Related
Task:
Create a public interface called IBoolS with a single method called
GetBools: Parameter int N, return value IEnumerable .
Create a public class called Generator that implements this interface
with a boolean property called ValueToGenerate, and the GetBools
method here should return N pieces of ValueToGenerate.
This is what I've tried so far:
IBoolS.cs:
public interface IBoolS
{
public IEnumerable<bool> GetBools(int N)
{
//what goes here? yield return something, but what?
}
}
Generator.cs:
public class Generator : IBoolS
{
private bool ValueToGenerate;
//GetBools(N pieces of ValueToGenerate);, but should be an integer number
}
Interface is just a declaration, a contract for the class(es) which implement it:
public interface IBoolS
{
IEnumerable<bool> GetBools(int N);
}
class implements the interface:
public class Generator : IBoolS
{
// Let's have a property instead of field: which value is generated
public bool ValueToGenerate {get;}
// Do not forget a constructor - we have to set ValueToGenerate
public Generator(bool valueToGenerate) {
ValueToGenerate = valueToGenerate;
}
// Interface implementation: we generate N bool values
public IEnumerable<bool> GetBools(int N) {
// Input validation
if (N < 0)
throw new ArgumentOutOfRangeException(nameof(N));
// Shorter version is
// return Enumerable.Repeat(ValueToGenerate, N);
for (int i = 0; i < N; ++i)
yield return ValueToGenerate;
}
}
Sounds like all you need is Enumerable.Repeat:
public interface IBoolS
{
IEnumerable<bool> GetBools(int N);
}
public class Generator : IBoolS
{
public bool ValueToGenerate { get; set; }
public IEnumerable<bool> GetBools(int N) => Enumerable.Repeat(ValueToGenerate, N);
}
Here's a possible implementation (not a performant one though):
public interface IBoolS
{
IEnumerable<bool> GetBools(int N);
}
public class Generator : IBoolS
{
private bool _valueToGenerate;
public Generator(bool valueToGenerate) {
_valueToGenerate = valueToGenerate;
}
public IEnumerable<bool> GetBools(int N) {
foreach (var n in Enumerable.Range(0, N)) {
yield return _valueToGenerate;
}
}
}
I've got a List<Card>, and I want to sort these cards
So, I'm looking for a method to sort them with different criterias, like their ID, their Name ...
public class Card : IComparer
{
public string ID;
public string Name;
public int CompareId(object firstCard, object secondCard)
{
Card c1 = (Card)firstCard;
Card c2 = (Card)secondCard;
return c1.Id.CompareTo(c2.Id);
}
}
But then, visual studio sent me an error :
'Card' does not implement interface member 'IComparer<Card>.Compare(Card, Card)'
You, probably, want to have your class Comparable not a Comparator
public class Card : IComparable<Card>
{
public string ID;
public string Name;
public int CompareTo(Card other)
{
if (null == other)
return 1;
// string.Compare is safe when Id is null
return string.Compare(this.Id, other.Id);
}
}
then
List<Card> myList = ...
myList.Sort();
Edit: If you want to have several criteria to choose from, you have to implement several Comparers as separated classes, e.g.
public sealed class CardByIdComparer : IComparer<Card>
{
public int Compare(Card x, Card y)
{
if (object.ReferenceEquals(x, y))
return 0;
else if (null == x)
return -1;
else if (null == y)
return 1;
else
return string.Compare(x.Id, y.Id);
}
}
and when sorting provide the required:
List<Card> myList = ...
myList.Sort(new CardByIdComparer());
Edit 2: (inspired by spender's library). If you want to combine several comparers into one (i.e. use comparer1, on tie - comparer2 etc.)
public sealed class ComparerCombined<T> : IComparer<T> {
private IComparer<T>[] m_Comparers;
public ComparerCombined(params IComparer<T>[] comparers) {
if (null == comparers)
throw new ArgumentNullException(nameof(comparers));
m_Comparers = comparers
.Select(item => item == null ? Comparer<T>.Default : item)
.Where(item => item != null)
.Distinct()
.ToArray();
}
public int Compare(T x, T y) {
if (object.ReferenceEquals(x, y))
return 0;
else if (null == x)
return -1;
else if (null == y)
return 1;
foreach (var comparer in m_Comparers) {
int result = comparer.Compare(x, y);
if (result != 0)
return result;
}
return 0;
}
}
usage:
myList.Sort(new ComparerCombined(
new CardByIdComparer(), // Sort By Id
new CardByNameComparer() // On tie (equal Id's) sort by name
));
The easiest way You can use Linq:
List<Card> objSortedList = objListObject.OrderBy(o=>o.ID).ToList();
or
List<Card> objSortedList = objListObject.OrderByDescending(o=>o.ID).ToList();
Good examples for demonstrate the concept of
List<T>.Sort(IComparer <T>) method check the link please.
IComparer<T> in this example compare method used for strings IComparer<T>
but you can use this for ID(int) too.
using System;
using System.Collections.Generic;
class GFG : IComparer<string>
{
public int Compare(string x, string y)
{
if (x == null || y == null)
{
return 0;
}
// "CompareTo()" method
return x.CompareTo(y);
}
}
public class geek
{
public static void Main()
{
List<string> list1 = new List<string>();
// list elements
list1.Add("C++");
list1.Add("Java");
list1.Add("C");
list1.Add("Python");
list1.Add("HTML");
list1.Add("CSS");
list1.Add("Scala");
list1.Add("Ruby");
list1.Add("Perl");
int range = 4;
GFG gg = new GFG();
Console.WriteLine("\nSort a range with comparer:");
// sort the list within a
// range of index 1 to 4
// where range = 4
list1.Sort(1, range, gg);
Console.WriteLine("\nBinarySearch and Insert Dart");
// Binary Search and storing
// index value to "index"
int index = list1.BinarySearch(0, range,
"Dart", gg);
if (index < 0)
{
list1.Insert(~index, "Dart");
range++;
}
}
}
You need to implement IComparer
public int Compare(Card card1, Card card2)
{
if (card1.ID > card2.ID)
return 1; //move card1 up
if (card2.ID < card1.ID)
return -1; //move card2 up
return 0; //do nothing
}
TASK: Create a method that receives as argument a list of any type that can be compared and an element of the given type. The method should return the count of elements that are greater than the value of the given element. Modify your Box class to support comparing by value of the data stored.
On the first line you will receive n - the number of elements to add to the list. On the next n lines, you will receive the actual elements. On the last line you will get the value of the element to which you need to compare every element in the list.
INPUT:
3
aa
aaa
bb
(givenElement is "aa")
OUTPUT:
2
Here is my code, i dunno how to compare them ...
public class Box<T> : IComparable<Box<T>>
{
private T value;
public Box(T value)
{
this.value = value;
}
public T Value
{
get
{
return this.value;
}
set
{
this.value = value;
}
}
public int CompareTo(Box<T> other)
{
// ???
}
public override string ToString()
{
return string.Format($"{value.GetType().FullName}: {value}");
}
}
static void Main()
{
int n = int.Parse(Console.ReadLine());
List<Box<string>> boxList = new List<Box<string>>();
for (int i = 0; i < n; i++)
{
string input = Console.ReadLine();
var box = new Box<string>(input);
boxList.Add(box);
}
string inp = Console.ReadLine();
var elementComparer = new Box<string>(inp);
GenericCountMethod(boxList, elementComparer);
}
public static int GenericCountMethod<T>(List<T> boxList, T str)
where T : IComparable<T>
{
int count = 0;
foreach (var item in boxList)
{
var x = item.CompareTo(str);
// ??
}
return count;
}
You could apply a constraint to a class
public class Box<T> : IComparable<Box<T>> where T: IComparable<T>
and then use CompareTo implementation of value:
public int CompareTo(Box<T> other)
{
return value.CompareTo(other.Value);
}
I have a list and in the list there are multiple entries. If the list contains an entry that is duplicated then I want to only keep one of the duplicates.
I've tried many things, the list.Distinct().ToList() and this does not remove the duplicate entry, I do not want to override the classes Equals method, so is there a way outside of that.
I've also done this method which seems to again, not remove the duplicate entry as it does not consider object a == object b.
private void removeDupes(List<Bookings> list)
{
int duplicates = 0;
int previousIndex = 0;
for (int i = 0; i < list.Count; i++)
{
bool duplicateFound = false;
for (int x = 0; x < i; x++)
{
if (list[i] == list[x])
{
duplicateFound = true;
duplicates++;
break;
}
}
if (duplicateFound == false)
{
list[previousIndex] = list[i];
previousIndex++;
}
}
}
There is another overload of the Distinct LINQ extension method that also takes an IEqualityComparer as an argument (see this link). So you'd need to create a class that implements IEqualityComparer<Bookings> and supply an instance of it to the Distinct-method. This way, you do not need to override the Equals method of the type.
The rules on whether two objects are equal to one another are implemented in the EqualityComparer.
As an alternative, you can use a HashSet and supply the EqualityComparer in the constructor.
A possible solution for your problem in order of Markus answer might look like this:
public class Booking
{
public Booking(int id, float amount)
{
BookingId = id;
BookingAmount = amount;
}
public int BookingId { get; }
public float BookingAmount { get; }
}
public class BookingComparer : IEqualityComparer<Booking>
{
public bool Equals(Booking x, Booking y)
{
return (x.BookingAmount == y.BookingAmount) && (x.BookingId == y.BookingId);
}
public int GetHashCode(Booking obj)
{
return obj.BookingId.GetHashCode()*17 + obj.BookingAmount.GetHashCode()*17;
}
}
internal class Program
{
private static void Main(string[] args)
{
var booking1 = new Booking(1, 12);
var booking2 = new Booking(1, 12);
var bookings = new List<Booking>();
bookings.Add(booking1);
bookings.Add(booking2);
var result = bookings.Distinct(new BookingComparer()).ToList();
}
}
I have an unsorted List<WordCount>
class WordCount
{
string word;
int count;
}
And now I must display the top 20 items in descending order of count. How could I code this efficiently? Currently I would set a minimum integer of -1 (all count >= 1) and do a for loop of 20 iterations with a foreach loop inside. This is an issue though because the last few elements in the List could have count of 1 while the top few may have an element with count 1 so now I am stuck on the pseudocode for this implementation for displaying them in order.
I CANNOT use LINQ or any other things other than the methods for List class. I personally think I must accomplish this feat using Sort() and CompareTo() somehow. This is meant to be a brain twister and that is the reason why it has to be done using the given restriction.
This should work:
List<WordCount> counts = new List<WordCount>();
//Fill the list
var result = counts.OrderBy(c => c.Count).Take(20);
Descending order:
var result = counts.OrderByDescending(c => c.Count).Take(20);
[Edit] Using self-made methods:
Here's a solution without any .NET method. First sort the list using an algorithm, in this case I used the Bubblesort (not effeicient for larger collections). Then I take the 20 first element from the sorted result:
public class WordCount
{
public string Word { get; set; }
public int CharCount { get; set; }
}
public List<WordCount> SortList(List<WordCount> list)
{
WordCount temp;
for (int i = list.Count -1; i >= 1; i--)
{
for (int j = 0; j < list.Count -1; j++)
{
if(list[j].CharCount < list[j+1].CharCount)
{
temp = list[j];
list[j] = list[j+1];
list[j+1] = temp;
}
}
}
return list;
}
public List<WordCount> TakeNItems(int n, List<WordCount> list)
{
List<WordCount> temp = new List<WordCount>();
for(int i = 0; i < n; i++)
temp.Add(list[i]);
return temp;
}
//Usage:
var result = SortList(counts);
result = TakeNItems(20, result);
[Edit2] Using Sort() / CompareTo()
Yes, it is also possible using Sort() and CompareTo(). This requieres a couple of changes to your class because when you try to use Sort() now, you'll get an InvalidOperationException. This is because the WordCount class does not implement the IComparable interface. Implementing the interface means you'll have to override the Equals() and GetHashCode() methods and provide your own comparer. Here's a simple implementation based on the List(T).Sort Method:
public class WordCount : IComparable<WordCount>
{
public string Word { get; set; }
public int CharCount { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
return false;
WordCount wc = obj as WordCount;
return wc == null ? false : Equals(wc);
}
public int CompareTo(WordCount wc)
{
//Descending
return wc == null ? 1 : wc.CharCount.CompareTo(CharCount);
//Ascending
//return wc == null ? 1 : CharCount.CompareTo(wc.CharCount);
}
public override int GetHashCode()
{
return CharCount;
}
public bool Equals(WordCount wc)
{
return wc == null ? false : CharCount.Equals(wc.CharCount);
}
}
//Usage:
List<WordCount> counts = new List<WordCount>();
//Fill the list
counts.Sort();
And for the limit of 20 items you can write your own extension method which would basically do the same as the Enumerable.Take Method:
public static class Extensions
{
public static IEnumerable<T> TakeN<T>(this List<T> list, int n)
{
for(int i = 0; i < n; i++)
yield return list[i];
}
}
//Usage:
List<WordCount> counts = new List<WordCount>();
//Fill the list with 10000 items and call TakeN()
IEnumerable<WordCount> smallList = counts.TakeN(20);
//Or
counts = counts.TakeN(20).ToList();
Hope this clarifies it all! ;)
The most straight-forward solution, using System.Linq:
var words = new List<WordCount>();
var result = from w in words orderby w.count descending select w.word;
result = result.Take(20);
This is most convenient and clear solution, so when possible use Linq. Also the result will be an IEnumerable<WordCount>, so compiler can do optimizations such as lazy enumeration, not calculating all elements until asked for them.