Comparing two list [duplicate] - c#

This question already has answers here:
Comparing two collections for equality irrespective of the order of items in them
(21 answers)
Closed 5 years ago.
Yet another list-comparing question.
List<MyType> list1;
List<MyType> list2;
I need to check that they both have the same elements, regardless of their position within the list. Each MyType object may appear multiple times on a list. Is there a built-in function that checks this? What if I guarantee that each element appears only once in a list?
EDIT: Guys thanks for the answers but I forgot to add something, the number of occurrences of each element should be the same on both lists.

If you want them to be really equal (i.e. the same items and the same number of each item), I think that the simplest solution is to sort before comparing:
Enumerable.SequenceEqual(list1.OrderBy(t => t), list2.OrderBy(t => t))
Edit:
Here is a solution that performs a bit better (about ten times faster), and only requires IEquatable, not IComparable:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2) {
var cnt = new Dictionary<T, int>();
foreach (T s in list1) {
if (cnt.ContainsKey(s)) {
cnt[s]++;
} else {
cnt.Add(s, 1);
}
}
foreach (T s in list2) {
if (cnt.ContainsKey(s)) {
cnt[s]--;
} else {
return false;
}
}
return cnt.Values.All(c => c == 0);
}
Edit 2:
To handle any data type as key (for example nullable types as Frank Tzanabetis pointed out), you can make a version that takes a comparer for the dictionary:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer) {
var cnt = new Dictionary<T, int>(comparer);
...

If you don't care about the number of occurrences, I would approach it like this. Using hash sets will give you better performance than simple iteration.
var set1 = new HashSet<MyType>(list1);
var set2 = new HashSet<MyType>(list2);
return set1.SetEquals(set2);
This will require that you have overridden .GetHashCode() and implemented IEquatable<MyType> on MyType.

As written, this question is ambigous. The statement:
... they both have the same elements, regardless of their position within the list.
Each MyType object may appear multiple times on a list.
does not indicate whether you want to ensure that the two lists have the same set of objects or the same distinct set.
If you want to ensure to collections have exactly the same set of members regardless of order, you can use:
// lists should have same count of items, and set difference must be empty
var areEquivalent = (list1.Count == list2.Count) && !list1.Except(list2).Any();
If you want to ensure two collections have the same distinct set of members (where duplicates in either are ignored), you can use:
// check that [(A-B) Union (B-A)] is empty
var areEquivalent = !list1.Except(list2).Union( list2.Except(list1) ).Any();
Using the set operations (Intersect, Union, Except) is more efficient than using methods like Contains. In my opinion, it also better expresses the expectations of your query.
EDIT: Now that you've clarified your question, I can say that you want to use the first form - since duplicates matter. Here's a simple example to demonstrate that you get the result you want:
var a = new[] {1, 2, 3, 4, 4, 3, 1, 1, 2};
var b = new[] { 4, 3, 2, 3, 1, 1, 1, 4, 2 };
// result below should be true, since the two sets are equivalent...
var areEquivalent = (a.Count() == b.Count()) && !a.Except(b).Any();

In addition to Guffa's answer, you could use this variant to have a more shorthanded notation.
public static bool ScrambledEquals<T>(this IEnumerable<T> list1, IEnumerable<T> list2)
{
var deletedItems = list1.Except(list2).Any();
var newItems = list2.Except(list1).Any();
return !newItems && !deletedItems;
}

Thinking this should do what you want:
list1.All(item => list2.Contains(item)) &&
list2.All(item => list1.Contains(item));
if you want it to be distinct, you could change it to:
list1.All(item => list2.Contains(item)) &&
list1.Distinct().Count() == list1.Count &&
list1.Count == list2.Count

This is a slightly difficult problem, which I think reduces to: "Test if two lists are permutations of each other."
I believe the solutions provided by others only indicate whether the 2 lists contain the same unique elements. This is a necessary but insufficient test, for example
{1, 1, 2, 3} is not a permutation of {3, 3, 1, 2}
although their counts are equal and they contain the same distinct elements.
I believe this should work though, although it's not the most efficient:
static bool ArePermutations<T>(IList<T> list1, IList<T> list2)
{
if(list1.Count != list2.Count)
return false;
var l1 = list1.ToLookup(t => t);
var l2 = list2.ToLookup(t => t);
return l1.Count == l2.Count
&& l1.All(group => l2.Contains(group.Key) && l2[group.Key].Count() == group.Count());
}

This worked for me:
If you are comparing two lists of objects depend upon single entity like ID, and you want a third list which matches that condition, then you can do the following:
var list3 = List1.Where(n => !List2.select(n1 => n1.Id).Contains(n.Id));
Refer: MSDN - C# Compare Two lists of objects

I use this method )
public delegate bool CompareValue<in T1, in T2>(T1 val1, T2 val2);
public static bool CompareTwoArrays<T1, T2>(this IEnumerable<T1> array1, IEnumerable<T2> array2, CompareValue<T1, T2> compareValue)
{
return array1.Select(item1 => array2.Any(item2 => compareValue(item1, item2))).All(search => search)
&& array2.Select(item2 => array1.Any(item1 => compareValue(item1, item2))).All(search => search);
}

try this!!!
using following code you could compare one or many fields to generate a result list as per your need. result list will contain only modified item(s).
// veriables been used
List<T> diffList = new List<T>();
List<T> gotResultList = new List<T>();
// compare First field within my MyList
gotResultList = MyList1.Where(a => !MyList2.Any(a1 => a1.MyListTField1 == a.MyListTField1)).ToList().Except(gotResultList.Where(a => !MyList2.Any(a1 => a1.MyListTField1 == a.MyListTField1))).ToList();
// Generate result list
diffList.AddRange(gotResultList);
// compare Second field within my MyList
gotResultList = MyList1.Where(a => !MyList2.Any(a1 => a1.MyListTField2 == a.MyListTField2)).ToList().Except(gotResultList.Where(a => !MyList2.Any(a1 => a1.MyListTField2 == a.MyListTField2))).ToList();
// Generate result list
diffList.AddRange(gotResultList);
MessageBox.Show(diffList.Count.ToString);

Related

Checking Similar Values in a List C#

I have the following code in C# which checks if items in a list are equal (a list of integers for all practical purposes):
intList.Distinct().Count() == 1
The above code works if all values in the list are equal.
However if I have a scenario where:
intList = {1, 1, 0}
then the above code fails. Can someone please suggest how I can check similar values in a list when all values in the list many not be equal.
How about
bool isDup = intList.Count == intList.Distinct().Count();
if (intList.Distinct().Count() != intList.Count)
{
// the list contained at least two values that were the same
}
Linq Group() and Count() the groups
bool allUnique = intList.GroupBy(x => x).All(x => x.Count() == 1);
by using HashSet()
bool allUnique = new HashSet<int>(intList).Count == intList.Count;

Get matched and unmatched elements from 2 lists

I have a Class named Privilegeswith the following properties int UserId,string FormName,string CompName,int Privilege
And I have 2 lists of Privileges type with different values as the sample below
List<Privileges> list1 = new List<Privileges>(){
new Privileges(){UserId= 1,FormName="Form1",CompName="Button1",Privilege=2},
new Privileges(){UserId= 2,FormName="Form1",CompName="Button3",Privilege=3},
new Privileges(){UserId= 3,FormName="Form2",CompName="Button2",Privilege=2}
};
List<Privileges> list2 = new List<Privileges>(){
new Privileges(){UserId= 5,FormName="Form1",CompName="Button1",Privilege=2},
new Privileges(){UserId= 2,FormName="Form1",CompName="Button3",Privilege=4},
new Privileges(){UserId= 4,FormName="Form2",CompName="Button2",Privilege=3}
};
I want to make 3 functions
I made the first one which returns matched elements between the 2 lists
and the result is the following
{UserId= 2,FormName="Form1",CompName="Button3",Privilege=3}
The 2nd function should return elements that exist in the first list and not in the second list, with the following result
{UserId= 1,FormName="Form1",CompName="Button1",Privilege=2},
{UserId= 3,FormName="Form2",CompName="Button2",Privilege=2}
The 3rd function should return elements that exist in the second list and not in the first list, with the following result
{UserId= 5,FormName="Form1",CompName="Button1",Privilege=2},
{UserId= 4,FormName="Form2",CompName="Button2",Privilege=3}
The matching clause should compare UserId,FormName,CompName values regardless what the value of privilege is.
you can check my code snippet here
You don't have to write any complex LINQ statements for these (and many more) tasks. Just define an IEqualityComparer and everything becomes almost ridiculously simple:
class PrivilegesComparer : IEqualityComparer<Privileges>
{
public bool Equals(Privileges x, Privileges y)
{
return x.UserId == y.UserId
&& x.FormName == y.FormName
&& x.CompName == y.CompName;
}
public int GetHashCode(Privileges obj)
{
return (obj.UserId + obj.FormName + obj.CompName).GetHashCode();
}
}
Usage:
var comparer = new PrivilegesComparer();
var intersect = list1.Intersect(list2, comparer);
var l1Exceptl2 = list1.Except(list2, comparer);
var l2Exceptl1 = list2.Except(list1, comparer);
Which represent your first, second and third function, respectively.
That's quite different from writing a complex LINQ statement for each individual task.
Elements in list1 not in list2
var itemsInList1NotInList2 = list1.Where(l1 => !list2.Any(l2 => l1.UserId == l2.UserId && l1.FormName == l2.FormName && l1.CompName == l2.CompName)).ToList();
Elements in list2 not in list1
var itemsInList2NotInList1 = list2.Where(l2 => !list1.Any(l1 => l1.UserId == l2.UserId && l1.FormName == l2.FormName && l1.CompName == l2.CompName)).ToList();

Linq Order by a specific number first then show all rest in order

If i have a list of numbers:
1,2,3,4,5,6,7,8
and I want to order by a specific number and then show the rest.
For example if i pick '3' the list should be:
3,1,2,4,5,6,7,8
Looking for linq and c#.
Thank you
You can use a comparison in OrderBy or ThenBy to perform a conditional sorting.
list.OrderByDescending(i => i == 3).ThenBy(i => i);
I use OrderByDescending because i want matching results first(true is "higher" than false).
A couple of answers already sort the last few numbers (which may be correct since you're only showing an already sorted list). If you want the "unselected" numbers to be displayed in their original, not necessarily sorted order instead of sorted, you can instead do;
int num = 3;
var result = list.Where(x => x == num).Concat(list.Where(x => x != num));
As #DuaneTheriot points out, IEnumerable's extension method OrderBy does a stable sort and won't change the order of elements that have an equal key. In other words;
var result = list.OrderBy(x => x != 3);
works just as well to sort 3 first and keep the order of all other elements.
Maybe something like this:
List<int> ls=new List<int>{1,2,3,4,5,6,7,8};
int nbr=3;
var result= ls.OrderBy (l =>(l==nbr?int.MinValue:l));
public static IEnumerable<T> TakeAndOrder<T>(this IEnumerable<T> items, Func<T, bool> f)
{
foreach ( var item in items.Where(f))
yield return item;
foreach (var item in items.Where(i=>!f(i)).OrderBy(i=>i))
yield return item;
}
var items = new [] {1, 4, 2, 5, 3};
items.TakeAndOrder(i=> i == 4);
Using #joachim-isaksson idea I came up with this extension method:
public static IOrderedEnumerable<TSource> OrderByWithGivenValueFirst<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
TKey value
)
=> source.OrderBy(x => !keySelector(x).Equals(value));
Test:
[TestFixture]
public class when_ordering_by_with_given_value_first
{
[Test]
public void given_value_is_first_in_the_collection()
{
var languages = new TestRecord[] {new("cs-CZ"), new("en-US"), new("de-DE"), new("sk-SK")};
languages.OrderByWithGivenValueFirst(x => x.Language, "en-US")
.ShouldBe(new TestRecord[] {new("en-US"), new("cs-CZ"), new("de-DE"), new("sk-SK")});
}
private record TestRecord(string Language);
}
You can try with below code with list of dynamic string values
var defaultSortingInternalTrades = ["E,F,G"];
var ItemsToSort = ["A","B","C","D","E",...];
var fData = items.Where(d => defaultSortingInternalTrades.Contains(d.ToString()))
.OrderBy(x => defaultSortingInternalTrades.IndexOf(x.ToString())).ToList();
var oData = items.Where(d => !defaultSortingInternalTrades.Contains(d.ToString())).ToList();
fData.AddRange(oData);

What is the shortest way to compare if two IEnumerable<T> have the same items in C#? [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Test whether two IEnumerable<T> have the same values with the same frequencies
I wrote
UPDATED - correction:
static bool HaveSameItems<T>(this IEnumerable<T> self, IEnumerable<T> other)
{
return !
(
other.Except(this).Any() ||
this.Except(other).Any()
);
}
Isn't there a shorter way?
I know there is SequenceEqual but the order doesn't matter for me.
Even if the order doesn't matter to you, it doesn't rule out SequenceEqual as a viable option.
var lst1 = new [] { 2,2,2,2 };
var lst2 = new [] { 2,3,4,5 };
var lst3 = new [] { 5,4,3,2 };
//your current function which will return true
//when you compare lst1 and lst2, even though
//lst1 is just a subset of lst2 and is not actually equal
//as mentioned by Wim Coenen
(lst1.Count() == lst2.Count() &&
!lst1.Except(lst2).Any()); //incorrectly returns true
//this also only checks to see if one list is a subset of another
//also mentioned by Wim Coenen
lst1.Intersect(lst2).Any(); //incorrectly returns true
//So even if order doesn't matter, you can make it matter just for
//the equality check like so:
lst1.OrderBy(x => x).SequenceEqual(lst2.OrderBy(x => x)); //correctly returns false
lst3.OrderBy(x => x).SequenceEqual(lst2.OrderBy(x => x)); // correctly returns true
Here's an O(n) solution that only walks each sequence once (in fact, it might not even completely walk the second sequence, it has early termination possibilities):
public static bool HaveSameItems<T>(IEnumerable<T> a, IEnumerable<T> b) {
var dictionary = a.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
foreach(var item in b) {
int value;
if (!dictionary.TryGetValue(item, out value)) {
return false;
}
if (value == 0) {
return false;
}
dictionary[item] -= 1;
}
return dictionary.All(x => x.Value == 0);
}
One downside to this solution is that it's not going to interop with LINQ to SQL, EF, NHiberate etc. nicely.

Compare two List<T> objects for equality, ignoring order [duplicate]

This question already has answers here:
Comparing two collections for equality irrespective of the order of items in them
(21 answers)
Closed 5 years ago.
Yet another list-comparing question.
List<MyType> list1;
List<MyType> list2;
I need to check that they both have the same elements, regardless of their position within the list. Each MyType object may appear multiple times on a list. Is there a built-in function that checks this? What if I guarantee that each element appears only once in a list?
EDIT: Guys thanks for the answers but I forgot to add something, the number of occurrences of each element should be the same on both lists.
If you want them to be really equal (i.e. the same items and the same number of each item), I think that the simplest solution is to sort before comparing:
Enumerable.SequenceEqual(list1.OrderBy(t => t), list2.OrderBy(t => t))
Edit:
Here is a solution that performs a bit better (about ten times faster), and only requires IEquatable, not IComparable:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2) {
var cnt = new Dictionary<T, int>();
foreach (T s in list1) {
if (cnt.ContainsKey(s)) {
cnt[s]++;
} else {
cnt.Add(s, 1);
}
}
foreach (T s in list2) {
if (cnt.ContainsKey(s)) {
cnt[s]--;
} else {
return false;
}
}
return cnt.Values.All(c => c == 0);
}
Edit 2:
To handle any data type as key (for example nullable types as Frank Tzanabetis pointed out), you can make a version that takes a comparer for the dictionary:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer) {
var cnt = new Dictionary<T, int>(comparer);
...
If you don't care about the number of occurrences, I would approach it like this. Using hash sets will give you better performance than simple iteration.
var set1 = new HashSet<MyType>(list1);
var set2 = new HashSet<MyType>(list2);
return set1.SetEquals(set2);
This will require that you have overridden .GetHashCode() and implemented IEquatable<MyType> on MyType.
As written, this question is ambigous. The statement:
... they both have the same elements, regardless of their position within the list.
Each MyType object may appear multiple times on a list.
does not indicate whether you want to ensure that the two lists have the same set of objects or the same distinct set.
If you want to ensure to collections have exactly the same set of members regardless of order, you can use:
// lists should have same count of items, and set difference must be empty
var areEquivalent = (list1.Count == list2.Count) && !list1.Except(list2).Any();
If you want to ensure two collections have the same distinct set of members (where duplicates in either are ignored), you can use:
// check that [(A-B) Union (B-A)] is empty
var areEquivalent = !list1.Except(list2).Union( list2.Except(list1) ).Any();
Using the set operations (Intersect, Union, Except) is more efficient than using methods like Contains. In my opinion, it also better expresses the expectations of your query.
EDIT: Now that you've clarified your question, I can say that you want to use the first form - since duplicates matter. Here's a simple example to demonstrate that you get the result you want:
var a = new[] {1, 2, 3, 4, 4, 3, 1, 1, 2};
var b = new[] { 4, 3, 2, 3, 1, 1, 1, 4, 2 };
// result below should be true, since the two sets are equivalent...
var areEquivalent = (a.Count() == b.Count()) && !a.Except(b).Any();
In addition to Guffa's answer, you could use this variant to have a more shorthanded notation.
public static bool ScrambledEquals<T>(this IEnumerable<T> list1, IEnumerable<T> list2)
{
var deletedItems = list1.Except(list2).Any();
var newItems = list2.Except(list1).Any();
return !newItems && !deletedItems;
}
Thinking this should do what you want:
list1.All(item => list2.Contains(item)) &&
list2.All(item => list1.Contains(item));
if you want it to be distinct, you could change it to:
list1.All(item => list2.Contains(item)) &&
list1.Distinct().Count() == list1.Count &&
list1.Count == list2.Count
This is a slightly difficult problem, which I think reduces to: "Test if two lists are permutations of each other."
I believe the solutions provided by others only indicate whether the 2 lists contain the same unique elements. This is a necessary but insufficient test, for example
{1, 1, 2, 3} is not a permutation of {3, 3, 1, 2}
although their counts are equal and they contain the same distinct elements.
I believe this should work though, although it's not the most efficient:
static bool ArePermutations<T>(IList<T> list1, IList<T> list2)
{
if(list1.Count != list2.Count)
return false;
var l1 = list1.ToLookup(t => t);
var l2 = list2.ToLookup(t => t);
return l1.Count == l2.Count
&& l1.All(group => l2.Contains(group.Key) && l2[group.Key].Count() == group.Count());
}
This worked for me:
If you are comparing two lists of objects depend upon single entity like ID, and you want a third list which matches that condition, then you can do the following:
var list3 = List1.Where(n => !List2.select(n1 => n1.Id).Contains(n.Id));
Refer: MSDN - C# Compare Two lists of objects
I use this method )
public delegate bool CompareValue<in T1, in T2>(T1 val1, T2 val2);
public static bool CompareTwoArrays<T1, T2>(this IEnumerable<T1> array1, IEnumerable<T2> array2, CompareValue<T1, T2> compareValue)
{
return array1.Select(item1 => array2.Any(item2 => compareValue(item1, item2))).All(search => search)
&& array2.Select(item2 => array1.Any(item1 => compareValue(item1, item2))).All(search => search);
}
try this!!!
using following code you could compare one or many fields to generate a result list as per your need. result list will contain only modified item(s).
// veriables been used
List<T> diffList = new List<T>();
List<T> gotResultList = new List<T>();
// compare First field within my MyList
gotResultList = MyList1.Where(a => !MyList2.Any(a1 => a1.MyListTField1 == a.MyListTField1)).ToList().Except(gotResultList.Where(a => !MyList2.Any(a1 => a1.MyListTField1 == a.MyListTField1))).ToList();
// Generate result list
diffList.AddRange(gotResultList);
// compare Second field within my MyList
gotResultList = MyList1.Where(a => !MyList2.Any(a1 => a1.MyListTField2 == a.MyListTField2)).ToList().Except(gotResultList.Where(a => !MyList2.Any(a1 => a1.MyListTField2 == a.MyListTField2))).ToList();
// Generate result list
diffList.AddRange(gotResultList);
MessageBox.Show(diffList.Count.ToString);

Categories

Resources