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();
}
}
Related
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
}
I am building a number of elements, all of which draw from the same data set, but which require different filters. Some need to have the value summed, others need the values counted, others need the value filtered.
So for an incredibly simple example, the base ViewModel class is this:
public class ViewModel {
private static DataModel _data;
public List<ListModel> CountItems { get; set; } = new List<ListModel>();
public List<ListModel> SumItems { get; set; } = new List<ListModel>();
public List<ListModel> FilterItems { get; set; } = new List<ListModel>();
public ViewModel () { }
public ViewModel (DataModel data) {
_data = data;
GetData(CountItems, Count);
GetData(SumItems, Sum(x => x.Data));
GetData(FilterItems, FirstOrDefault(x => x.Criteria == Value).Select(x => x.Data));
}
private void GetData<T1, T2>(T1 list1, T2 [DoSomething]) where T1 : List<ListModel> where T2 [requirements] {
for(var i = 0; i < 12, i++){
list1.Add(new ListModel(PrepareData(i).[DoSomething]));
}
}
// This is used in many other places, it cannot be touched.
private static IList<DataModel> PrepareData(i) => _data.[do some generic operations using the i].ToList();
}
public class ListModel {
public string Value { get; set; }
public ListModel() { }
public ListModel(int? i) => Value = i is 0 || i is null ? string.Empty : i.ToString();
}
(The actual setup is a lot more complicated, but this is it boiled down to the essentials)
The _data grabs the in-memory data set, and prepares it for more than one lambda operation. So, I want to be able to do this:
GetData(CountItems, Count);
GetData(SumItems, Sum(x => x.Data));
GetData(FilterItems, FirstOrDefault(x => x.Criteria == Value).Select(x => x.Data));
And have the anonymous function behave something like this:
private void GetData<T1, T2>(T1 list1, T2 p) where T1 : List<ListModel> {
for(var i = 0; i < 12, i++){
list1.Add(new ListModel(PrepareData(i).p));
}
}
That way, all I need to do is put in the Lambda chunk required, be it a Sum(), Count(), Where() or even a FirstOrDefault(), and get the appropriate single-value output. In fact, I am trying to build it as an anonymous method so that it can handle almost any input!
Please understand that I am not asking to pass in the contents of an action, such as what is inside a .where(), but the ENTIRE .where() statement itself!!
Any guidance would be greatly appreciated.
EDIT: Corrected the above code to include a new ListModel that catches the value. This may have been screwing up some answers below.
EDIT 2:
Even when I do something really simple:
private static void GetData<T1>(T1 list1, Func<IEnumerable<Incident>, T2> p) where T1 : List<ListModel> {
for(var i = 0; i <= 13; i++) {
var value = new int?(p(PrepareData(i)));
}
It says that T2 cannot be converted into an int.
Pass a Func<IEnumerable<T>,TRes> to GetData:
private void GetData<T,TRes>(IList<TRes> list, Func<IEnumerable<T>,TRes> aggregator) where {
for (var i = 0 ; i < 12 ; i++) {
list1.Add(aggregate(PrepareData(i)));
}
}
You can call this method as follows:
GetData(CountItems, list => list.Count());
GetData(SumItems, list => list.Sum(x => x.Data));
The second parameter to GetData should be a Func<IEnumerable<DataModel>,int> or you can fix the generics and make int a type parameter.
Now GetData will look like:
private void GetData<T1>(List<T1> list1, Func<IEnumerable<DataModel>,T1> p)
{
for(var i = 0; i < 12, i++)
{
list1.Add(p(PrepareData(i)));
}
}
Now you can call it like:
GetData(CountItems, Count);
GetData(SumItems, t => t.Sum(x => x.Data));
GetData(FilterItems, t => t.FirstOrDefault(x => x.Data == Value)?.Data)
EDIT
If you are expecting the result to always be an int? to pass to a new ListModel, then you can just drop the generics altogether:
private void GetData(List<ListModel> list1, Func<IEnumerable<DataModel>,int?> p)
{
for(var i = 0; i < 12, i++)
{
list1.Add(new ListModel(p(PrepareData(i))));
}
}
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.
How to create dynamic incrementing variable using "for" loop in C#? like this:
track_1, track_2, track_3, track_4. so on.
You can't create dynamically-named variables. All you can do - it to create some collection or array, and operate with it.
I think the best class for you is generic List<>:
List<String> listWithDynamic = new List<String>();
for (int i = 1; i < limit; i +=1)
{
listWithDynamic.Add(string.Format("track_{0}", i));
...
}
Assuming you want strings:
for (int i = 1; i < limit; i +=1)
{
string track = string.Format("track_{0}", i);
...
}
But when you already have variables called track_1, track_2, track_3, track_4 you will need an array or List:
var tracks = new TrackType[] { track_1, track_2, track_3, track_4 } ;
for (int i = 0; i < tracks.length; i++)
{
var track = tracks[i]; // tracks[0] == track_1
...
}
Obvious Solution
for (var i = 0; i < 10; i++)
{
var track = string.Format("track_{0}", i);
}
Linq-Based Solution
foreach (var track in Enumerable.Range(0, 100).Select(x => string.Format("track_{0}", x)))
{
}
Operator-Based Solution This is somewhat hacky, but fun none-the-less.
for (var i = new Frob(0, "track_{0}"); i < 100; i++)
{
Console.WriteLine(i.ValueDescription);
}
struct Frob
{
public int Value { get; private set; }
public string ValueDescription { get; private set; }
private string _format;
public Frob(int value, string format)
: this()
{
Value = value;
ValueDescription = string.Format(format, value);
_format = format;
}
public static Frob operator ++(Frob value)
{
return new Frob(value.Value + 1, value._format);
}
public static Frob operator --(Frob value)
{
return new Frob(value.Value - 1, value._format);
}
public static implicit operator int(Frob value)
{
return value.Value;
}
public static implicit operator string(Frob value)
{
return value.ValueDescription;
}
public override bool Equals(object obj)
{
if (obj is Frob)
{
return ((Frob)obj).Value == Value;
}
else if (obj is string)
{
return ((string)obj) == ValueDescription;
}
else if (obj is int)
{
return ((int)obj) == Value;
}
else
{
return base.Equals(obj);
}
}
public override int GetHashCode()
{
return Value;
}
public override string ToString()
{
return ValueDescription;
}
}
don't know if I get your question, but I will try:
for(var i = 1; i < yourExclusiveUpperbound; i++)
{
var track = String.Format("$track_{0}", i);
// use track
}
or with some LINQ-Magic:
foreach(var track in Enumerate.Range(1, count)
.Select(i => String.Format("$track_{0}", i)))
{
// use track
}
Do as follow:
for (int i = 0; i < lenght; i ++)
{
any work do in loop
}
No, we can't create dynamically named variables in a loop. But, there are other elegant ways to address the problem instead of creating dynamically named variables.
One could be, create an array or list before the loop and store values in array / list items in the loop. You can access the array / list later anywhere in your code. If you know which variable you want to use (track_1, track_2, ...), you can simply access it from the array / list (tracks[1], tracks[2], ...).
List<String> tracks = new List<String>();
for (int i = 1; i < limit; i++)
{
Track track = new Track();
tracks.Add(track);
...
}
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