Jeff Johnson
Jeff Johnson

Reputation: 1097

Filter and keep first object of a List of objects with properties that match

I apologize upfront, because I now realize that I have completely worded my example wrong. For those who have given responses, I truly appreciate it. Please let me re-attempt to explain with a more accurate details. Please edit your responses, and once again, I apologize for not being more exact in my previous posting.

Using an entity framework model class called Staging (which is a representation of my Staging table), I have the following List<Staging>.

List<Staging> data = (from t in database.Stagings select t).ToList();
//check for an empty List...react accordingly...

Here is a quick look at what Staging looks like:

public partial class Staging
{
    public int ID { get; set; }        //PK
    public int RequestID { get; set; } //FK
    ...
    public string Project { get; set; }
    ...
}

Let us suppose that the query returns 10 records into my data list. Let us also suppose that data[3], data[6], and data[7] each have the same value in data.Project, let's say "Foo". The data.Project value is not known until runtime.

Given this, how would I keep the first occurrence, data[3], and remove data[6] and data[7] from my List<Staging>?

Edit: I have the following code that works, but is there another way?

  HashSet<string> projectValuesFound = new HashSet<string>();
  List<Staging> newData = new List<Staging>();

  foreach (Staging entry in data)
  {
    if (!projectValuesFound.Contains(entry.Project))
    {
      projectValuesFound.Add(entry.Project);
      newData.Add(entry);
    }
  }

Upvotes: 1

Views: 1028

Answers (4)

Branko Dimitrijevic
Branko Dimitrijevic

Reputation: 52117

The "canonical" way to do it would be to pass appropriately implemented comparer to Distinct:

class Var3Comparer : IEqualityComparer<MyClass> {
    public int GetHashCode(MyClass obj) {
        return (obj.Var3 ?? string.Empty).GetHashCode();
    }
    public bool Equals(MyClass x, MyClass y) {
        return x.Var3 == y.Var3;
    }
}

// ...

var distinct = list.Distinct(new Var3Comparer());

Just beware that while current implementation seems to keep the ordering of the "surviving" elements, the documentation says it "returns an unordered sequence" and is best treated that way.

There is also a Distinct overload that doesn't require a comparer - it just assumes the Default comparer, which in turn, will utilize the IEquatable<T> if implemented by MyClass.

Upvotes: 0

Eric J.
Eric J.

Reputation: 150108

You could use Linq:

var result = (from my in theList where my.Var3 == "Foo" select my).First();

If you also want to keep the other items, you can use Distinct() instead of First(). To use Dictinct(), either MyClass must implement IEquatable<T>, or you must provide an IEqualityComparer<T> as shown in the MSDN link.

Upvotes: 0

Brian P
Brian P

Reputation: 1587

var uniques = (from theList select theList.Var3).Distinct();

That will give you distinct values for all entries.

Upvotes: 0

Reed Copsey
Reed Copsey

Reputation: 564433

You can do this via LINQ and a HashSet<T>:

var found = new HashSet<string>();
var distinctValues = theList.Where(mc => found.Add(mc.Var3));

// If you want to assign back into the List<T> again:
// theList = distinctValues.ToList();

This works because HashSet<T>.Add returns true if the value was not already in the set, and false if it already existed. As such, you'll only get the first "matching" value for Var3.

Upvotes: 2

Related Questions