I have 2 Lists available to me. I need to gather the data that is no longer used.
For Example
List 1:
1
2
3
4
5
List 2:
1
2
4
5
6
The result data set needs to be.
Items not Included in List 2:
3
I was hoping to use something along the lines of:
var itemsNotInList2 = List2.Except(List1).ToList();
You're dealing with List<int> in this example then you have the right idea, just the args reversed. It should be;
var itemsNotInList2 = List1.Except(List2).ToList();
Think about how to state this in plain English. To get itemsNotInList2 I want to take everything in List1 except what's in List2. Your code in the question is giving you items that are in List2 but not in List1 which there are none of since List2 is a subset of List1
Note that this approach is often not suitable for reference types because the default comaparer will compare the references themselves. In order to do a similar operate with objects you'd have to implement IEqualityComparer and invoke the overload which accepts that as it's third argument. For example if you were dealing with a List<Person> and Person had a public string Ssid you could define Equal with return p1.Ssid == p2.Ssid and use that as your basis of comparison. You can find examples of this on msdn should you need it.
public class Person
{
public string Ssid;
// other properties and methods
}
public class PersonSsidEqualityComparer : IEqualityComparer<Person>
{
public bool Equal(Person lhs, Person rhs)
{
return lhs.Ssid == rhs.Ssid
}
public int GetHashCode(Person p)
{
return p.Value.GetHashCode();
}
}
Now as an example;
List<Person> people = new List<Person>();
List<Person> otherPeople = new List<Person>();
Person p1 = new Person("123"); // pretend this constructor takes an ssid
Person p2 = new Person("123");
Person p3 = new Person("124");
Person p4 = p1;
Now some examples using the data I set up above;
people.Add(p1);
people.Add(p3);
otherPeople.Add(p2);
var ThemPeople = people.Except(otherPeople);
// gives you p1 and p3
var ThemOtherPeople = people.Except(otherPeople, new PersonSsidEqualityComparar());
// only gives you p3
otherPeople.Add(p4);
var DoingReferenceComparesNow = people.Except(otherPeople);
// gives you only p3 cause p1 == p4 (they're the same address)
Try this
var itemsNotInList2 = List1.Except(List2).ToList();
If you're comparing objects, you should probably provide your own Equality Comparer.
For example:
public class YourClass
{
public int Value;
}
public class YourClassEqualityComparer : IEqualityComparer<YourClass>
{
public bool Equals(YourClass x, YourClass y)
{
return x.Value == y.Value;
}
public int GetHashCode(YourClass obj)
{
return obj.Value.GetHashCode();
}
}
So you can use an overload of Except that takes an instance of your equality comparer:
var list = l1.Except(l2, new YourClassEqualityComparer());
Related
I'm desperately trying to delete all the items with a list of the same value inside.
Here's the code:
private void Button_deleteDouble_MouseDown(object sender, EventArgs e)
{
boardGenerate.Add(new BoardInformation(146, new List<string> { "test" }));
boardGenerate.Add(new BoardInformation(545, new List<string> { "test" }));
boardGenerate = boardGenerate.DistinctBy(x => x.positionQueen).ToList();
}
Normally, since the two lists inside the object are the same, the .DistinctBy() command should remove one of the two objects.
But no, my object list still has the same two objects with the same list
.positionQueen is the name of the variable containing the list
Could somebody help me?
Edit :
The DistinctBy() method comes from MoreLinq.
And this is my BoardInformation class:
public class BoardInformation
{
public BoardInformation(int nbQueen, List<string> positionQueen)
{
this.nbQueen = nbQueen;
this.positionQueen = positionQueen;
}
public int nbQueen { get; set; }
public List<string> positionQueen { get; set; }
}
Set-based operations like Distinct and DistinctBy need a way of determining whether two values are the same. You're using DistinctBy, so you're already asking MoreLINQ to compare the "inner lists" for equality - but you're not saying how to do that.
List<T> doesn't override Equals or GetHashCode, which means it inherits the reference equality behaviour from System.Object. In other words, if you create two separate List<T> objects, they won't compare as equal, even if they have the same content. For example:
List<int> list1 = new List<int>();
List<int> list2 = new List<int>();
Console.WriteLine(list1.Equals(list2)); // False
You need to tell DistinctBy how you want to compare the two lists, using an IEqualityComparer<T> - where T in this case is List<string> (because that's the type of BoardInformation.positionQueen.
Here's an example of a generic ListEqualityComparer you could use:
using System;
using System.Collections.Generic;
using System.Linq;
public sealed class ListEqualityComparer<T> : IEqualityComparer<List<T>>
{
private readonly IEqualityComparer<T> elementComparer;
public ListEqualityComparer(IEqualityComparer<T> elementComparer) =>
this.elementComparer = elementComparer;
public ListEqualityComparer() : this(EqualityComparer<T>.Default)
{
}
public bool Equals(List<T> x, List<T> y) =>
ReferenceEquals(x, y) ? true
: x is null || y is null ? false
// Delegate to LINQ's SequenceEqual method
: x.SequenceEqual(y, elementComparer);
public int GetHashCode(List<T> obj)
{
if (obj is null)
{
return 0;
}
// Just a very simple hash implementation
int hash = 23;
foreach (var item in obj)
{
hash = hash * 31 +
(item is null ? 0
: elementComparer.GetHashCode(item));
}
return hash;
}
}
You'd then pass that to DistinctBy, like this:
// We're fine to use the default *element* comparer (string.Equals etc)
var comparer = new ListEqualityComparer<string>();
boardGenerate = boardGenerate.DistinctBy(x => x.positionQueen, comparer).ToList();
Now DistinctBy will call into the comparer, passing in the lists, and will consider your two BoardInformation objects are equal - so only the first will be yielded by DistinctBy, and you'll end up with a list containing a single item.
It comes down to whether a equality check is using referential equality or value equality...you want value equality based on a specific property and that has to be done by hand.
When there is no IEqualityComparer provided which can used to compare individual objects (which is need by the Distinct call), the system determines the equality from each item's references by using their derived object low level service method call of GetHashCode from each reference; hence a reference difference is done and all your values in the list are unique (not equal) regardless of similar property values.
What you are looking for is to have value equality checked specifically for the nbQueenProperty.
To fully utilize Distinct one must create a IEqualityComparer and modify the GetHashCode. By specifing the hash value which can make objects equal...you can weed out the same positionQueen (or other properties) instances out.
Example
public class MyClass
{
public string Name { get; set; }
public int nbQueen { get; set; }
}
Equality comparer to weed out all nbQueen similarities:
class ContactEmailComparer : IEqualityComparer < MyClass >
{
public bool Equals(MyClass x, MyClass y)
{
return x.nbQueen.Equals(y.nbQueen); // Compares by calling each `GetHashCode`
}
public int GetHashCode(MyClass obj)
{
return obj.nbQueen.GetHashCode(); // Add or remove other properties as needed.
}
}
Test code
var original = new List<MyClass>()
{
new MyClass() { nbQueen = 1, Name="Alpha" },
new MyClass() { nbQueen = 1, Name="Omega" },
new MyClass() { nbQueen = 3, Name="Delta" }
};
IEqualityComparer<MyClass> comparer = new ContactEmailComparer();
var newOne = original.Distinct( comparer ).ToList();
Result of the value of newOne :
To be clear...
... .DistinctBy() command should remove one of the two objects.
Does not remove anything. It returns a reference to a new list that should be distinct via the equality operation. The original list (the reference to it) does not change.
LINQ solution
because you have another List inside your class you can not use District or DistrictBy, alternatively, you can use LINQ to filter the list.
boardGenerate = (from b in boardGenerate
from l in b.positionQueen
group new { l,b } by l into g
select g.First().b
).ToList();
// this returns just first duplicate item like district
I have a little strange problem. I use Visual Studio and I am developing a project with C#.
I have two custom classes "Attr" and "FD" and I use lists that includes their objects e.g.
List<Attr> attrList = new List<Attr>();
List<FD> fdList = new List<FD>();
So when I try to find the intersection of two lists the result is not what I expect. To make it more simple I tried to Intersect similar Objects and the result is wrong again. What is going wrong?
This is the fd. It is an object of class FD.
This is the ff which is also an object of FD class.
As you can see these object contains exactly the same values.
The method GetLeft() returns a list that contains objects of class Attr.
So when I try to find the intersection between those two lists (fd.GetLeft() and ff.GetLeft() ) the result is nothing (it should be a list that contains an Attr object "A").
What did I miss?
P.S. These screenshots are from the debugg mode in Visual Studio.
In order to use Intersect I suggest implementing IEqualityComparer<T>, something like this :
public class FD
{
public string Name { get; set; }
}
static void Main()
{
List<FD> fdList1 = new List<FD>();
fdList1.Add(new FD { Name = "a" });
List<FD> fdList2 = new List<FD>();
fdList2.Add(new FD { Name = "a" });
IEnumerable<FD> fd = fdList1.Intersect<FD>(fdList2, new ComparerFd()).ToList();
}
And the CamparerFd should be like this :
public class ComparerFd : IEqualityComparer<FD>
{
public bool Equals(FD x, FD y)
{
return x.Name == y.Name;
}
public int GetHashCode(FD obj)
{
if(obj == null) return 0;
return obj.Name.GetHashCode();//Or whatever way to get hash code
}
}
If you created your own class, and did not override the Equals-method in that class, the Intersect-method will only compare the references of the objects, and not the properties.
Take the following, really simple class:
class MyClass
{
int Value { get; set; }
public MyClass(int value)
{
this.Value = value;
}
}
Now, create two lists, with both containing one object. The properties of the objects are the same, but the instances are not:
var list1 = new List<MyClass>
{
new MyClass(5)
};
var list2 = new List<MyClass>
{
new MyClass(5)
};
So the following will happen:
list1[0].Equals(list2[0]); // false
list1.Intersect(list2); // No matches
If you want these methods to compare the properties of your MyClass-objects, implement IEqualityComparer<MyClass>, e.g. change the classes signature to:
class MyClass : IEqualityComparer<MyClass>
{
..
}
Alternatively, you can just override Equals and GetHashCode, as then these methods will be called as default IEqualityComparer.
See the this answer on how to properly override Equals and GetHashCode.
I am trying to sort an ArrayList using c#. When the ArrayList contains comparable objects, it is possible to sort with using list.Sort() but I need to sort an ArrayList which contains non-comparable objects. For example, let's say the object is Ring and it has an attribute property Price. Then I need to sort the ArrayList to the price order. If is is possible to select ascending or descending that will more helpful. Thank You!
Blockquote
arrAtdMon = **(ArrayList)**hashTb[unixMon];
if (arrAtdMon != null)
monCount = arrAtdMon.Count;
int[] arrayMax = { monCount, tueCount, wedCount, thuCount, friCount };
int maxValue = arrayMax.Max();
KidAttendance valMon = null;
string monTagName = string.Empty;
Blockquote
above array list is to be sorted it self.
You can do this by implementing IComparer interface:-
public class Ring : IComparer
{
public decimal Price { get; set; }
public int Compare(object x, object y)
{
return ((Ring)x).Price.CompareTo(((Ring)y).Price);
}
}
Working Fiddle.
First, you really should be using the List<T> class, not ArrayList. Doing so wouldn't solve your problem, but it would make the code less fragile and more easy to maintain.
As for the specific question, you want to do something like this…
Assume:
class Ring { public decimal Price { get; set; } }
Then:
ArrayList list = ...; // Initialized as some collection of Ring instances
list.Sort(Comparer.Create((r1, r2) => r1.Price.CompareTo(r2.Price)));
This creates a new Comparer instance using the Comparison<T> of (r1, r2) => r1.Price.CompareTo(r2.Price). That is, for each pair of objects being compared, compare the price of the first with the price of the second.
Assuming that these objects share a base class or an interface with the price property you should be able to do something like this:
// Base class with price property, could also be an shared interface
public abstract class Product
{
public decimal Price{get;set;}
}
public class Ring : Product
{
}
public class Bag : Product
{
}
// Some test data
var myUnsortedArray = new Product[]{new Ring{Price = 1.2m}, new Bag{Price=2.5m}};
// Easy sort with LINQ
var sortedProducts = myUnsortedArray.OrderBy(p => p.Price).ToArray();
var sortedProductsDescending = myUnsortedArray.OrderByDescending(p => p.Price).ToArray();
UPDATE
I just realised that the question is about ArrayLists and have the changed solution below:
// Some test data
var myUnsortedArrayList = new ArrayList{new Ring{Price = 1.2m}, new Bag{Price=2.5m}};
// Easy sort with LINQ
var sortedProducts = myUnsortedArrayList.OfType<Product>().OrderBy(p => p.Price).ToArray();
var sortedProductsDescending = myUnsortedArrayList.OfType<Product>().OrderByDescending(p => p.Price).ToArray();
To sort an set of objects, the object needs to be Comparable and you can set up the comparison you'd like in the CompareTo() method:
IComparable information here
Probably this question was already asked before, but my google-fu and SO-Search did not get me what what I was looking for.
I have a custom class, and a custom class comparer (for checking the equality of the class) implemented with IEqualityComparer.
public class Person
{
public string Name { get; set; }
public bool Flag { get; set; }
}
public class PersonComparer : IEqualityComparer<Person>
{
#region IEqualityComparer<Person> Members
public bool Equals(Person x, Person y)
{
//case insensitive compare
return string.Equals(x.Name, y.Name, StringComparison.OrdinalIgnoreCase);
}
public int GetHashCode(Person obj)
{
return base.GetHashCode();
}
#endregion
}
and in the main portion of the code I have 2 lists "source" and "target"
Person bob = new Person() { Name = "Bob" };
Person sam = new Person() { Name = "Sam" };
Person andy = new Person() { Name = "Andy" };
Person thomas = new Person() { Name = "Thomas" };
Person jimmy = new Person() { Name = "Jimmy" };
Person sam2 = new Person() { Name = "sam" }; // note the lower case
Person jane = new Person() { Name = "Jane" };
List<Person> source = new List<Person>() { bob, sam, andy, thomas };
List<Person> target = new List<Person>() { sam2, andy,jane };
what I want to do
update source list to only contain sam and andy, as bob and thomas are not in the target list. I did this
source = (from p in source where (from t in target select t)
.Contains(p, new PersonComparer())
select p).ToList();
In the target I should "Flag" sam2 and andy to true and jane is flagged as "false" by default, I should not change it.
I tried using this, but this removes "jane" from target
//sets sam2 & andy to true, removes Jane
target = (from p in target.Select(t => { t.Flag = true; return t; })
where (from s in source
select s).Intersect(select p).ToList();
Can any LINQ guru tell me what I am doing wrong ?
3.Is there a better way to write Query 1 ?
4.And finally a trivial question: how exactly do you say "=>" when you are talking to a fellow coder over the phone
As Sander has pointed out, LINQ is for querying, not updating.
However, to answer the questions... Your original query of
source = (from p in source where (from t in target select t)
.Contains(p, new PersonComparer()) select p).ToList();
would be much more simply written as:
source = source.Intersect(target, new PersonComparer()).ToList();
Having said that, you need to update PersonComparer as recursive mentioned. It should be something like this:
public int GetHashCode(Person obj)
{
return obj == null ? 0
: StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Name);
}
I'm afraid I don't really understand your second query particularly well... but if you want to change the existing objects, I'd suggest a foreach loop instead of trying to use LINQ. Queries with side-effects are generally a bad idea.
You may mean something like:
// You may want to make some singleton instance available, as this has no state
PersonComparer comparer = new PersonComparer();
foreach (Person person in target)
{
if (source.Contains(person, comparer))
{
person.Flag = true;
}
}
Linq isn't meant to update list, because it operates on IEnumerable<T>. You can create a new enumerable, based on source and target that represents the collection you need.
Something like this should work:
var combined = source.Where(x => target.Any(y => y == x))
For part 4. => can be read as goes to.
The GetHashCode() method should use the the obj passed instance, not its own parent.
I have an example class containing two data points:
public enum Sort { First, Second, Third, Fourth }
public class MyClass
{
public MyClass(Sort sort, string name) {
this.Sort = sort;
this.Name = name;
}
public Sort Sort { get; set; }
public string Name { get; set; }
}
I'm looking to sort them into groups by their Sort property, and then alphabetize those groups.
List<MyClass> list = new List<MyClass>() {
new MyClass(MyClass.Sort.Third, "B"),
new MyClass(MyClass.Sort.First, "D"),
new MyClass(MyClass.Sort.First, "A"),
new MyClass(MyClass.Sort.Fourth, "C"),
new MyClass(MyClass.Sort.First, "AB"),
new MyClass(MyClass.Sort.Second, "Z"),
};
The output would then be:
(showing Name)
A
AB
D
Z
B
C
I've been able to do this by using a foreach to separate the items into many smaller arrays (grouped by the enum value) but this seems very tedious - and I think there must be some LINQ solution that I don't know about.
Using extension methods, first OrderBy the enum, ThenBy name.
var sorted = list.OrderBy( m => m.Sort ).ThenBy( m => m.Name );
Aside from the nice LINQ solutions, you can also do this with a compare method like you mentioned. Make MyClass implement the IComparable interface, with a CompareTo method like:
public int CompareTo(object obj)
{
MyClass other = (MyClass)obj;
int sort = this.srt.CompareTo(other.srt);
return (sort == 0) ? this.Name.CompareTo(other.Name) : sort;
}
The above method will order your objects first by the enum, and if the enum values are equal, it compares the name. Then, just call list.Sort() and it will output the correct order.
This should do it, I think
var result = from m in list
orderby m.Sort, m.Name
select m;