C# List<> Sort by x then y - c#

Similar to List<> OrderBy Alphabetical Order, we want to sort by one element, then another. we want to achieve the functional equivalent of
SELECT * from Table ORDER BY x, y
We have a class that contains a number of sorting functions, and we have no issues sorting by one element.
For example:
public class MyClass {
public int x;
public int y;
}
List<MyClass> MyList;
public void SortList() {
MyList.Sort( MySortingFunction );
}
And we have the following in the list:
Unsorted Sorted(x) Desired
--------- --------- ---------
ID x y ID x y ID x y
[0] 0 1 [2] 0 2 [0] 0 1
[1] 1 1 [0] 0 1 [2] 0 2
[2] 0 2 [1] 1 1 [1] 1 1
[3] 1 2 [3] 1 2 [3] 1 2
Stable sort would be preferable, but not required. Solution that works for .Net 2.0 is welcome.

For versions of .Net where you can use LINQ OrderBy and ThenBy (or ThenByDescending if needed):
using System.Linq;
....
List<SomeClass>() a;
List<SomeClass> b = a.OrderBy(x => x.x).ThenBy(x => x.y).ToList();
Note: for .Net 2.0 (or if you can't use LINQ) see Hans Passant answer to this question.

Do keep in mind that you don't need a stable sort if you compare all members. The 2.0 solution, as requested, can look like this:
public void SortList() {
MyList.Sort(delegate(MyClass a, MyClass b)
{
int xdiff = a.x.CompareTo(b.x);
if (xdiff != 0) return xdiff;
else return a.y.CompareTo(b.y);
});
}
Do note that this 2.0 solution is still preferable over the popular 3.5 Linq solution, it performs an in-place sort and does not have the O(n) storage requirement of the Linq approach. Unless you prefer the original List object to be untouched of course.

The trick is to implement a stable sort. I've created a Widget class that can contain your test data:
public class Widget : IComparable
{
int x;
int y;
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
public Widget(int argx, int argy)
{
x = argx;
y = argy;
}
public int CompareTo(object obj)
{
int result = 1;
if (obj != null && obj is Widget)
{
Widget w = obj as Widget;
result = this.X.CompareTo(w.X);
}
return result;
}
static public int Compare(Widget x, Widget y)
{
int result = 1;
if (x != null && y != null)
{
result = x.CompareTo(y);
}
return result;
}
}
I implemented IComparable, so it can be unstably sorted by List.Sort().
However, I also implemented the static method Compare, which can be passed as a delegate to a search method.
I borrowed this insertion sort method from C# 411:
public static void InsertionSort<T>(IList<T> list, Comparison<T> comparison)
{
int count = list.Count;
for (int j = 1; j < count; j++)
{
T key = list[j];
int i = j - 1;
for (; i >= 0 && comparison(list[i], key) > 0; i--)
{
list[i + 1] = list[i];
}
list[i + 1] = key;
}
}
You would put this in the sort helpers class that you mentioned in your question.
Now, to use it:
static void Main(string[] args)
{
List<Widget> widgets = new List<Widget>();
widgets.Add(new Widget(0, 1));
widgets.Add(new Widget(1, 1));
widgets.Add(new Widget(0, 2));
widgets.Add(new Widget(1, 2));
InsertionSort<Widget>(widgets, Widget.Compare);
foreach (Widget w in widgets)
{
Console.WriteLine(w.X + ":" + w.Y);
}
}
And it outputs:
0:1
0:2
1:1
1:2
Press any key to continue . . .
This could probably be cleaned up with some anonymous delegates, but I'll leave that up to you.
EDIT: And NoBugz demonstrates the power of anonymous methods...so, consider mine more oldschool :P

This may help you,
How to Sort C# Generic List

I had an issue where OrderBy and ThenBy did not give me the desired result (or I just didn't know how to use them correctly).
I went with a list.Sort solution something like this.
var data = (from o in database.Orders Where o.ClientId.Equals(clientId) select new {
OrderId = o.id,
OrderDate = o.orderDate,
OrderBoolean = (SomeClass.SomeFunction(o.orderBoolean) ? 1 : 0)
});
data.Sort((o1, o2) => (o2.OrderBoolean.CompareTo(o1.OrderBoolean) != 0
o2.OrderBoolean.CompareTo(o1.OrderBoolean) : o1.OrderDate.Value.CompareTo(o2.OrderDate.Value)));

Related

Minimum reprocessing when comparing 2 identical list in c#

I've got two identical lists I need to compare so I thought I'd create a 2 dimensional array and store a bool value as to whether my condition passed or failed but I want to reduce the number of checks made against each element as it wouldn't make sense to:
a) compare the exact same elements (as this condition would always pass
in my scenario).
|0|1|2|
----------
|0|x| | |
|1| |x| |
|2| | |x|
----------
b) compare exact opposite elements in the array where if a condition
passed where i=1 and j=2 we'd know that i=2 and j=1 would also pass.
Array1: [2,6,9]
Array2: [2,6,9]
|0|1|2|
----------
|0| | | |
|1| | |p|
|2| |p| |
----------
Where p[2,1] would be (9,6) for example and p[1,2] would be (6,9) which
in my case would mean they are identical.
So based on the above is there a way to minimize the looping required or do I have to loop using something similar to this?
for (int i = 0; i < source.Count; i++)
{
for (int j = 0; j < compare.Count; j++)
{
if ((i != j) &&
!alreadyProcessed[i, j] && !alreadyProcessed[j, i])
{
alreadyProcessed[i, j] = true;
alreadyProcessed[j, i] = true;
bool condition = ...;
if (condition)
{
}
}
else
{
if (i == j)
{
alreadyProcessed[i, j] = true;
}
}
}
}
I was hoping that using linq and intersection for example that I could exclude the likes of (0,0), (1,1) and (2,2) for example and then somehow have a single combination of unique combinations as we would only need (0,1) and (0,2) and (1,2) as we'd know that (1,0), (2,0) and (2,1) would be the same as their opposite.
Hope it makes sense.
Since at any point i you can disregard everything in the list with an index smaller than i (equal would be the same element and smaller, we will already have checked), you can simply go with the following:
var array = new int[] {1, 2, 3, 4};
for (int i = 0; i < array.Count(); i++) {
for (int j = i + 1; j < array.Count(); j++) {
Console.WriteLine($"Comparirf {i}:{array[i]} and {j}:{array[j]}");
}
}
Linq doesn't really have the tools to do this efficiently (as far as I know at least).
Well, since the sequence is identical you are really talking about single sequence.
Below is a working sample of how to use LINQ to get the unique combinations from a sequence.
If you wanted to see if the pairs were equal, you could do
pairs.Select(p => p.First.Equals(p.Second));
Here is a Fiddle, below is the code
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var pairs = ((IEnumerable<int>)new[] { 1, 2, 3, 4 }).Pairs();
Console.WriteLine(
string.Join(
$",{Environment.NewLine}",
pairs.Select(p => $"{{first:{p.First}, second:{p.Second}}}")));
}
}
public static class Ext
{
public static IEnumerable<Pair<T>> Pairs<T>(
this IEnumerable<T> source)
{
return source.SelectMany(
(value, index) => source.Skip(index + 1),
(first, second) => new Pair<T>(first, second));
}
public sealed class Pair<T>
{
internal Pair(T first, T second)
{
this.First = first;
this.Second = second;
}
public T First { get; }
public T Second { get; }
}
}

How to sort a List<> by two criterias in C# [duplicate]

Similar to List<> OrderBy Alphabetical Order, we want to sort by one element, then another. we want to achieve the functional equivalent of
SELECT * from Table ORDER BY x, y
We have a class that contains a number of sorting functions, and we have no issues sorting by one element.
For example:
public class MyClass {
public int x;
public int y;
}
List<MyClass> MyList;
public void SortList() {
MyList.Sort( MySortingFunction );
}
And we have the following in the list:
Unsorted Sorted(x) Desired
--------- --------- ---------
ID x y ID x y ID x y
[0] 0 1 [2] 0 2 [0] 0 1
[1] 1 1 [0] 0 1 [2] 0 2
[2] 0 2 [1] 1 1 [1] 1 1
[3] 1 2 [3] 1 2 [3] 1 2
Stable sort would be preferable, but not required. Solution that works for .Net 2.0 is welcome.
For versions of .Net where you can use LINQ OrderBy and ThenBy (or ThenByDescending if needed):
using System.Linq;
....
List<SomeClass>() a;
List<SomeClass> b = a.OrderBy(x => x.x).ThenBy(x => x.y).ToList();
Note: for .Net 2.0 (or if you can't use LINQ) see Hans Passant answer to this question.
Do keep in mind that you don't need a stable sort if you compare all members. The 2.0 solution, as requested, can look like this:
public void SortList() {
MyList.Sort(delegate(MyClass a, MyClass b)
{
int xdiff = a.x.CompareTo(b.x);
if (xdiff != 0) return xdiff;
else return a.y.CompareTo(b.y);
});
}
Do note that this 2.0 solution is still preferable over the popular 3.5 Linq solution, it performs an in-place sort and does not have the O(n) storage requirement of the Linq approach. Unless you prefer the original List object to be untouched of course.
The trick is to implement a stable sort. I've created a Widget class that can contain your test data:
public class Widget : IComparable
{
int x;
int y;
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
public Widget(int argx, int argy)
{
x = argx;
y = argy;
}
public int CompareTo(object obj)
{
int result = 1;
if (obj != null && obj is Widget)
{
Widget w = obj as Widget;
result = this.X.CompareTo(w.X);
}
return result;
}
static public int Compare(Widget x, Widget y)
{
int result = 1;
if (x != null && y != null)
{
result = x.CompareTo(y);
}
return result;
}
}
I implemented IComparable, so it can be unstably sorted by List.Sort().
However, I also implemented the static method Compare, which can be passed as a delegate to a search method.
I borrowed this insertion sort method from C# 411:
public static void InsertionSort<T>(IList<T> list, Comparison<T> comparison)
{
int count = list.Count;
for (int j = 1; j < count; j++)
{
T key = list[j];
int i = j - 1;
for (; i >= 0 && comparison(list[i], key) > 0; i--)
{
list[i + 1] = list[i];
}
list[i + 1] = key;
}
}
You would put this in the sort helpers class that you mentioned in your question.
Now, to use it:
static void Main(string[] args)
{
List<Widget> widgets = new List<Widget>();
widgets.Add(new Widget(0, 1));
widgets.Add(new Widget(1, 1));
widgets.Add(new Widget(0, 2));
widgets.Add(new Widget(1, 2));
InsertionSort<Widget>(widgets, Widget.Compare);
foreach (Widget w in widgets)
{
Console.WriteLine(w.X + ":" + w.Y);
}
}
And it outputs:
0:1
0:2
1:1
1:2
Press any key to continue . . .
This could probably be cleaned up with some anonymous delegates, but I'll leave that up to you.
EDIT: And NoBugz demonstrates the power of anonymous methods...so, consider mine more oldschool :P
This may help you,
How to Sort C# Generic List
I had an issue where OrderBy and ThenBy did not give me the desired result (or I just didn't know how to use them correctly).
I went with a list.Sort solution something like this.
var data = (from o in database.Orders Where o.ClientId.Equals(clientId) select new {
OrderId = o.id,
OrderDate = o.orderDate,
OrderBoolean = (SomeClass.SomeFunction(o.orderBoolean) ? 1 : 0)
});
data.Sort((o1, o2) => (o2.OrderBoolean.CompareTo(o1.OrderBoolean) != 0
o2.OrderBoolean.CompareTo(o1.OrderBoolean) : o1.OrderDate.Value.CompareTo(o2.OrderDate.Value)));

using linq to find 2 values in a row

Is it easier to use linq to do the same thing as this code? (check and see how many values are equal to the value following):
int[] values = {1,2,3,3,5,6,7};
int counter=0;
for (int f =0; f< values.Length-1; f++)
{
if(values[f]==values[f+1])
{
counter++;
}
}
Yes, you can do this quite easily with Zip in .NET 4:
var count = values.Zip(values.Skip(1), (x, y) => new { x, y })
.Count(pair => pair.x == pair.y);
The trick of combining Zip and Skip(1) takes a little bit of getting your head round, but it's a really neat one. Basically you start with a sequence of n values, and the result is a sequence of n - 1 pairs, each of which contains a value and its successor.
From there, it's just a matter of counting the pairs which are the same :)
Note that the sequence in question will be evaluated twice, so you wouldn't want to do this for anything which was lazily evaluated, or which wouldn't give the same results when evaluated twice.
No I don't think it's easier. The code you have is easy to understand and concise, I wouldn't refactor it with linq. Make sure you test it a bit more though, as you might get an out of bounds error on the last loop.
There exists a very neat solution:
var list = new[] { 1, 2, 3, 3, 5, 6, 7, 7 };
var pairs = SeqModule.Pairwise(list);
var count = pairs.Count(p => p.Item1 == p.Item2);
This requires that you reference the assembly FSharp.Core and you use using Microsoft.FSharp.Collections;. Alternatively, you can implement the Pairwise method as a extension method and thereby avoid using another assembly.
For anyone who might be interested in F#, here is a solution:
let lst = [1;2;3;3;5;6;7;7]
let count = lst |> Seq.pairwise
|> Seq.filter (fun (x, y) -> x = y)
|> Seq.length
Try this:
var set = values.Where((value, i) => i < (values.Length - 1) ? values[i] == values[i + 1] : false);
EDIT:
Sorry, forgot to add set.Count() to get the result :-)
Given that the values are in an array, you can do this:
int duplicates = values.Skip(1).Where((n, i) => n == values[i]).Count();
You could say something like:
private static int CountDoubles( IEnumerable<int> Xs )
{
int? prev = null ;
int count = Xs.Count( curr => {
bool isMatch = prev.HasValue && prev == curr ;
prev = curr ;
return isMatch ;
} ) ;
return count ;
}
but that's neither simpler nor cleaner than your original version. I'd tweak yours slightly, though::
public static int CountDoubles( int[] Xs )
{
int n = 0 ;
for ( int i = 1 ; i < Xs.Length ; ++i )
{
n += ( Xs[i-1] == Xs[i] ? 1 : 0 ) ;
}
return n ;
}

Easy way to compare values of more than 3 variables?

I want to check whether these variables have same values.
EXAMPLE:
int a = 5;
int b = 5;
int c = 5;
int d = 5;
int e = 5;
. . .
int k = 5;
if(a==b && b==c && c==d && d==e && .... && j==k)
{
//this is Complex way and not well understandable.
}
Any easy way to Compare all are same?
LIKE in below example
if(a==b==c==d==e.....j==k)
{
//Understandable but not work
}
how about something like this:
if (Array.TrueForAll<int>(new int[] {a, b, c, d, e, f, g, h, i, j, k },
val => (a == val))) {
// do something
}
With this many variables, would it make sense to move them into an array?
You could then test to see if they are all equal using Linq expressions like myarray.Distinct().Count() == 1; or perhaps myarray.All(r => r == 5);
You could create a var args method to do that:
bool intsEqual (params int[] ints) {
for (int i = 0; i < ints.Length - 1; i++) {
if (ints[i] != ints[i+1]) {
return False;
}
}
return True;
}
Then just call it with all your ints as parameters:
if (intsEqual(a, b, c, d, e, f, g, h, i, j, k)) {
doSomeStuff();
}
Just a thought, but if you can calculate the standard deviation of the entire list, and it is equal to zero, you would have your answer.
Here's an answer on the site that addresses this that may help with that: Standard deviation of generic list?
Interesting problem. Good luck with it.
I agree that the easiest way is to place them all into a list and then use the following to compare. This is in essence looping through and comparing to the first value, but this is a little cleaner.
var match = counts.All(x => x == counts[0])
How about
int common = a;
if (a==common && b==common && c==common && d==common && .... && k==common)
You could write a helper method like this:
public static bool AllEqual<T> (params T[] values) where T : IEquatable<T>
{
if (values.Length < 2) return true;
for (int i = 1; i < values.Length; i++)
if (!values[i].Equals (values[0])) return false;
return true;
}
This will be subtly different to the == operator in special cases, though:
AllEqual (double.NaN, double.NaN).Dump(); // True
(double.NaN == double.NaN).Dump(); // False
It doesn't work because a==b evaluates to a boolean which can't be compared to an integer, c. What you have seems to be the best way.
You might consider putting the values in an array and using a for() loop. It isn't really any simpler, but it might help if the number of values changed.
You could use a variable argument helper function to perform the comparison pretty easily.
static bool CompareLongList(params int[] args)
{
if (args.Length > 1)
{
int value = args[0];
for (int i = 1; i < args.Length; ++i)
{
if (value != args[i])
return false;
}
}
return true;
}
Then you would just use the function as follows
if(CompareLongList(a,b,c,d,e,f,j,h,i,j,k))
{
// True Code
}
I know it's an old question I came across but I was wondering what's wrong with:
if (a == (b & c & d & e & f & g & h & i & j & k))
{
}
Compare the same elements in array:
same = len(uniq([1,2,3,4])) == 1
Use Linq Query.
var greatestInt = new List() { a,b,c,d,e,f}.Max();
Bitwise and is a possible way to check multiple variables for same value.
Such a helper method could of course also check for equality instead of using the '&' operator. Helper method accepting a params array of variables seems to be the right method here. We could adjust this method to accept a generic argument also, but there are only a few data types which support boolean logical operators anyways in C# (and other languages).
Testing with a high value of Int32 to check validity of this:
void Main()
{
int a = 1234567891;
int b = 1234567891;
int c = 1234567891;
int d = 1234567891;
int e = 1234567891;
int f = 1234567891;
int g = 1234567891;
int h = 1234567891;
int i = 1234567891;
int j = 1234567891;
int k = 1234567891;
bool areAllSameValue = IntUtils.AreAllVariablesSameValue(a,b,c,d,e,f,g,h,i,j,k);
areAllSameValue.Dump(); //Linqpad method - dump this code into Linqpad to test
}
public class IntUtils
{
public static bool AreAllVariablesSameValue(params int[] values)
{
if (values == null || !values.Any())
{
return false;
}
int bitWiseAndValue = values[0];
foreach (var value in values)
{
bitWiseAndValue &= value;
}
return bitWiseAndValue == values[0];
}
}
This spots also if one of the values got a different sign (negative number).

Remove duplicates from a List<T> in C#

Anyone have a quick method for de-duplicating a generic List in C#?
If you're using .Net 3+, you can use Linq.
List<T> withDupes = LoadSomeData();
List<T> noDupes = withDupes.Distinct().ToList();
Perhaps you should consider using a HashSet.
From the MSDN link:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
HashSet<int> evenNumbers = new HashSet<int>();
HashSet<int> oddNumbers = new HashSet<int>();
for (int i = 0; i < 5; i++)
{
// Populate numbers with just even numbers.
evenNumbers.Add(i * 2);
// Populate oddNumbers with just odd numbers.
oddNumbers.Add((i * 2) + 1);
}
Console.Write("evenNumbers contains {0} elements: ", evenNumbers.Count);
DisplaySet(evenNumbers);
Console.Write("oddNumbers contains {0} elements: ", oddNumbers.Count);
DisplaySet(oddNumbers);
// Create a new HashSet populated with even numbers.
HashSet<int> numbers = new HashSet<int>(evenNumbers);
Console.WriteLine("numbers UnionWith oddNumbers...");
numbers.UnionWith(oddNumbers);
Console.Write("numbers contains {0} elements: ", numbers.Count);
DisplaySet(numbers);
}
private static void DisplaySet(HashSet<int> set)
{
Console.Write("{");
foreach (int i in set)
{
Console.Write(" {0}", i);
}
Console.WriteLine(" }");
}
}
/* This example produces output similar to the following:
* evenNumbers contains 5 elements: { 0 2 4 6 8 }
* oddNumbers contains 5 elements: { 1 3 5 7 9 }
* numbers UnionWith oddNumbers...
* numbers contains 10 elements: { 0 2 4 6 8 1 3 5 7 9 }
*/
How about:
var noDupes = list.Distinct().ToList();
In .net 3.5?
Simply initialize a HashSet with a List of the same type:
var noDupes = new HashSet<T>(withDupes);
Or, if you want a List returned:
var noDupsList = new HashSet<T>(withDupes).ToList();
Sort it, then check two and two next to each others, as the duplicates will clump together.
Something like this:
list.Sort();
Int32 index = list.Count - 1;
while (index > 0)
{
if (list[index] == list[index - 1])
{
if (index < list.Count - 1)
(list[index], list[list.Count - 1]) = (list[list.Count - 1], list[index]);
list.RemoveAt(list.Count - 1);
index--;
}
else
index--;
}
Notes:
Comparison is done from back to front, to avoid having to resort list after each removal
This example now uses C# Value Tuples to do the swapping, substitute with appropriate code if you can't use that
The end-result is no longer sorted
I like to use this command:
List<Store> myStoreList = Service.GetStoreListbyProvince(provinceId)
.GroupBy(s => s.City)
.Select(grp => grp.FirstOrDefault())
.OrderBy(s => s.City)
.ToList();
I have these fields in my list: Id, StoreName, City, PostalCode
I wanted to show list of cities in a dropdown which has duplicate values.
solution: Group by city then pick the first one for the list.
It worked for me. simply use
List<Type> liIDs = liIDs.Distinct().ToList<Type>();
Replace "Type" with your desired type e.g. int.
As kronoz said in .Net 3.5 you can use Distinct().
In .Net 2 you could mimic it:
public IEnumerable<T> DedupCollection<T> (IEnumerable<T> input)
{
var passedValues = new HashSet<T>();
// Relatively simple dupe check alg used as example
foreach(T item in input)
if(passedValues.Add(item)) // True if item is new
yield return item;
}
This could be used to dedupe any collection and will return the values in the original order.
It's normally much quicker to filter a collection (as both Distinct() and this sample does) than it would be to remove items from it.
An extension method might be a decent way to go... something like this:
public static List<T> Deduplicate<T>(this List<T> listToDeduplicate)
{
return listToDeduplicate.Distinct().ToList();
}
And then call like this, for example:
List<int> myFilteredList = unfilteredList.Deduplicate();
In Java (I assume C# is more or less identical):
list = new ArrayList<T>(new HashSet<T>(list))
If you really wanted to mutate the original list:
List<T> noDupes = new ArrayList<T>(new HashSet<T>(list));
list.clear();
list.addAll(noDupes);
To preserve order, simply replace HashSet with LinkedHashSet.
This takes distinct (the elements without duplicating elements) and convert it into a list again:
List<type> myNoneDuplicateValue = listValueWithDuplicate.Distinct().ToList();
Use Linq's Union method.
Note: This solution requires no knowledge of Linq, aside from that it exists.
Code
Begin by adding the following to the top of your class file:
using System.Linq;
Now, you can use the following to remove duplicates from an object called, obj1:
obj1 = obj1.Union(obj1).ToList();
Note: Rename obj1 to the name of your object.
How it works
The Union command lists one of each entry of two source objects. Since obj1 is both source objects, this reduces obj1 to one of each entry.
The ToList() returns a new List. This is necessary, because Linq commands like Union returns the result as an IEnumerable result instead of modifying the original List or returning a new List.
As a helper method (without Linq):
public static List<T> Distinct<T>(this List<T> list)
{
return (new HashSet<T>(list)).ToList();
}
Here's an extension method for removing adjacent duplicates in-situ. Call Sort() first and pass in the same IComparer. This should be more efficient than Lasse V. Karlsen's version which calls RemoveAt repeatedly (resulting in multiple block memory moves).
public static void RemoveAdjacentDuplicates<T>(this List<T> List, IComparer<T> Comparer)
{
int NumUnique = 0;
for (int i = 0; i < List.Count; i++)
if ((i == 0) || (Comparer.Compare(List[NumUnique - 1], List[i]) != 0))
List[NumUnique++] = List[i];
List.RemoveRange(NumUnique, List.Count - NumUnique);
}
Installing the MoreLINQ package via Nuget, you can easily distinct object list by a property
IEnumerable<Catalogue> distinctCatalogues = catalogues.DistinctBy(c => c.CatalogueCode);
If you have tow classes Product and Customer and we want to remove duplicate items from their list
public class Product
{
public int Id { get; set; }
public string ProductName { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string CustomerName { get; set; }
}
You must define a generic class in the form below
public class ItemEqualityComparer<T> : IEqualityComparer<T> where T : class
{
private readonly PropertyInfo _propertyInfo;
public ItemEqualityComparer(string keyItem)
{
_propertyInfo = typeof(T).GetProperty(keyItem, BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
}
public bool Equals(T x, T y)
{
var xValue = _propertyInfo?.GetValue(x, null);
var yValue = _propertyInfo?.GetValue(y, null);
return xValue != null && yValue != null && xValue.Equals(yValue);
}
public int GetHashCode(T obj)
{
var propertyValue = _propertyInfo.GetValue(obj, null);
return propertyValue == null ? 0 : propertyValue.GetHashCode();
}
}
then, You can remove duplicate items in your list.
var products = new List<Product>
{
new Product{ProductName = "product 1" ,Id = 1,},
new Product{ProductName = "product 2" ,Id = 2,},
new Product{ProductName = "product 2" ,Id = 4,},
new Product{ProductName = "product 2" ,Id = 4,},
};
var productList = products.Distinct(new ItemEqualityComparer<Product>(nameof(Product.Id))).ToList();
var customers = new List<Customer>
{
new Customer{CustomerName = "Customer 1" ,Id = 5,},
new Customer{CustomerName = "Customer 2" ,Id = 5,},
new Customer{CustomerName = "Customer 2" ,Id = 5,},
new Customer{CustomerName = "Customer 2" ,Id = 5,},
};
var customerList = customers.Distinct(new ItemEqualityComparer<Customer>(nameof(Customer.Id))).ToList();
this code remove duplicate items by Id if you want remove duplicate items by other property, you can change nameof(YourClass.DuplicateProperty) same nameof(Customer.CustomerName) then remove duplicate items by CustomerName Property.
If you don't care about the order you can just shove the items into a HashSet, if you do want to maintain the order you can do something like this:
var unique = new List<T>();
var hs = new HashSet<T>();
foreach (T t in list)
if (hs.Add(t))
unique.Add(t);
Or the Linq way:
var hs = new HashSet<T>();
list.All( x => hs.Add(x) );
Edit: The HashSet method is O(N) time and O(N) space while sorting and then making unique (as suggested by #lassevk and others) is O(N*lgN) time and O(1) space so it's not so clear to me (as it was at first glance) that the sorting way is inferior
Might be easier to simply make sure that duplicates are not added to the list.
if(items.IndexOf(new_item) < 0)
items.add(new_item)
You can use Union
obj2 = obj1.Union(obj1).ToList();
Another way in .Net 2.0
static void Main(string[] args)
{
List<string> alpha = new List<string>();
for(char a = 'a'; a <= 'd'; a++)
{
alpha.Add(a.ToString());
alpha.Add(a.ToString());
}
Console.WriteLine("Data :");
alpha.ForEach(delegate(string t) { Console.WriteLine(t); });
alpha.ForEach(delegate (string v)
{
if (alpha.FindAll(delegate(string t) { return t == v; }).Count > 1)
alpha.Remove(v);
});
Console.WriteLine("Unique Result :");
alpha.ForEach(delegate(string t) { Console.WriteLine(t);});
Console.ReadKey();
}
There are many ways to solve - the duplicates issue in the List, below is one of them:
List<Container> containerList = LoadContainer();//Assume it has duplicates
List<Container> filteredList = new List<Container>();
foreach (var container in containerList)
{
Container duplicateContainer = containerList.Find(delegate(Container checkContainer)
{ return (checkContainer.UniqueId == container.UniqueId); });
//Assume 'UniqueId' is the property of the Container class on which u r making a search
if(!containerList.Contains(duplicateContainer) //Add object when not found in the new class object
{
filteredList.Add(container);
}
}
Cheers
Ravi Ganesan
Here's a simple solution that doesn't require any hard-to-read LINQ or any prior sorting of the list.
private static void CheckForDuplicateItems(List<string> items)
{
if (items == null ||
items.Count == 0)
return;
for (int outerIndex = 0; outerIndex < items.Count; outerIndex++)
{
for (int innerIndex = 0; innerIndex < items.Count; innerIndex++)
{
if (innerIndex == outerIndex) continue;
if (items[outerIndex].Equals(items[innerIndex]))
{
// Duplicate Found
}
}
}
}
David J.'s answer is a good method, no need for extra objects, sorting, etc. It can be improved on however:
for (int innerIndex = items.Count - 1; innerIndex > outerIndex ; innerIndex--)
So the outer loop goes top bottom for the entire list, but the inner loop goes bottom "until the outer loop position is reached".
The outer loop makes sure the entire list is processed, the inner loop finds the actual duplicates, those can only happen in the part that the outer loop hasn't processed yet.
Or if you don't want to do bottom up for the inner loop you could have the inner loop start at outerIndex + 1.
A simple intuitive implementation:
public static List<PointF> RemoveDuplicates(List<PointF> listPoints)
{
List<PointF> result = new List<PointF>();
for (int i = 0; i < listPoints.Count; i++)
{
if (!result.Contains(listPoints[i]))
result.Add(listPoints[i]);
}
return result;
}
All answers copy lists, or create a new list, or use slow functions, or are just painfully slow.
To my understanding, this is the fastest and cheapest method I know (also, backed by a very experienced programmer specialized on real-time physics optimization).
// Duplicates will be noticed after a sort O(nLogn)
list.Sort();
// Store the current and last items. Current item declaration is not really needed, and probably optimized by the compiler, but in case it's not...
int lastItem = -1;
int currItem = -1;
int size = list.Count;
// Store the index pointing to the last item we want to keep in the list
int last = size - 1;
// Travel the items from last to first O(n)
for (int i = last; i >= 0; --i)
{
currItem = list[i];
// If this item was the same as the previous one, we don't want it
if (currItem == lastItem)
{
// Overwrite last in current place. It is a swap but we don't need the last
list[i] = list[last];
// Reduce the last index, we don't want that one anymore
last--;
}
// A new item, we store it and continue
else
lastItem = currItem;
}
// We now have an unsorted list with the duplicates at the end.
// Remove the last items just once
list.RemoveRange(last + 1, size - last - 1);
// Sort again O(n logn)
list.Sort();
Final cost is:
nlogn + n + nlogn = n + 2nlogn = O(nlogn) which is pretty nice.
Note about RemoveRange:
Since we cannot set the count of the list and avoid using the Remove funcions, I don't know exactly the speed of this operation but I guess it is the fastest way.
Using HashSet this can be done easily.
List<int> listWithDuplicates = new List<int> { 1, 2, 1, 2, 3, 4, 5 };
HashSet<int> hashWithoutDuplicates = new HashSet<int> ( listWithDuplicates );
List<int> listWithoutDuplicates = hashWithoutDuplicates.ToList();
Using HashSet:
list = new HashSet<T>(list).ToList();
public static void RemoveDuplicates<T>(IList<T> list )
{
if (list == null)
{
return;
}
int i = 1;
while(i<list.Count)
{
int j = 0;
bool remove = false;
while (j < i && !remove)
{
if (list[i].Equals(list[j]))
{
remove = true;
}
j++;
}
if (remove)
{
list.RemoveAt(i);
}
else
{
i++;
}
}
}
If you need to compare complex objects, you will need to pass a Comparer object inside the Distinct() method.
private void GetDistinctItemList(List<MyListItem> _listWithDuplicates)
{
//It might be a good idea to create MyListItemComparer
//elsewhere and cache it for performance.
List<MyListItem> _listWithoutDuplicates = _listWithDuplicates.Distinct(new MyListItemComparer()).ToList();
//Choose the line below instead, if you have a situation where there is a chance to change the list while Distinct() is running.
//ToArray() is used to solve "Collection was modified; enumeration operation may not execute" error.
//List<MyListItem> _listWithoutDuplicates = _listWithDuplicates.ToArray().Distinct(new MyListItemComparer()).ToList();
return _listWithoutDuplicates;
}
Assuming you have 2 other classes like:
public class MyListItemComparer : IEqualityComparer<MyListItem>
{
public bool Equals(MyListItem x, MyListItem y)
{
return x != null
&& y != null
&& x.A == y.A
&& x.B.Equals(y.B);
&& x.C.ToString().Equals(y.C.ToString());
}
public int GetHashCode(MyListItem codeh)
{
return codeh.GetHashCode();
}
}
And:
public class MyListItem
{
public int A { get; }
public string B { get; }
public MyEnum C { get; }
public MyListItem(int a, string b, MyEnum c)
{
A = a;
B = b;
C = c;
}
}
I think the simplest way is:
Create a new list and add unique item.
Example:
class MyList{
int id;
string date;
string email;
}
List<MyList> ml = new Mylist();
ml.Add(new MyList(){
id = 1;
date = "2020/09/06";
email = "zarezadeh#gmailcom"
});
ml.Add(new MyList(){
id = 2;
date = "2020/09/01";
email = "zarezadeh#gmailcom"
});
List<MyList> New_ml = new Mylist();
foreach (var item in ml)
{
if (New_ml.Where(w => w.email == item.email).SingleOrDefault() == null)
{
New_ml.Add(new MyList()
{
id = item.id,
date = item.date,
email = item.email
});
}
}

Categories

Resources