私は次のものを持っていますEFクラスデータベースから派生しました(簡略化)
class Product
{
public string ProductId;
public string ProductName;
public string CategoryId;
public string CategoryName;
}
ProductId
は、テーブルのプライマリキーです。
DBデザイナーによる不適切な設計決定(変更できません)の場合、このテーブルにはCategoryId
とCategoryName
があります。
DropDownList with(distinct)CategoryId
as ValueおよびCategoryName
as Textが必要です。したがって、次のコードを適用しました。
product.Select(m => new {m.CategoryId, m.CategoryName}).Distinct();
論理的には、CategoryId
およびCategoryName
をプロパティとして持つ匿名オブジェクトを作成する必要があります。 Distinct()
は、重複ペアがないことを保証します(CategoryId
、CategoryName
)。
しかし、実際には機能しません。私が理解している限り、Distinct()
は、コレクションにフィールドが1つだけあるときに機能します。そうでない場合、フィールドは無視されます...それは正しいですか?回避策はありますか?ありがとう!
UPDATE
申し訳ありませんproduct
は:
List<Product> product = new List<Product>();
Distinct()
と同じ結果を得る別の方法を見つけました:
product.GroupBy(d => new {d.CategoryId, d.CategoryName})
.Select(m => new {m.Key.CategoryId, m.Key.CategoryName})
リストでメソッド呼び出しのようなdistinctを使用すると仮定します。クエリの結果をDropDownListのデータソースとして使用する必要があります。たとえば、ToList
を介してクエリを具体化します。
var distinctCategories = product
.Select(m => new {m.CategoryId, m.CategoryName})
.Distinct()
.ToList();
DropDownList1.DataSource = distinctCategories;
DropDownList1.DataTextField = "CategoryName";
DropDownList1.DataValueField = "CategoryId";
プロパティがほとんどない匿名型の代わりに実際のオブジェクトが必要な場合は、匿名型でGroupBy
を使用することもできます。
List<Product> distinctProductList = product
.GroupBy(m => new {m.CategoryId, m.CategoryName})
.Select(group => group.First()) // instead of First you can also apply your logic here what you want to take, for example an OrderBy
.ToList();
3番目のオプションは、 MoreLinqのDistinctBy
を使用することです。
Distinct()は、重複ペア(CategoryId、CategoryName)がないことを保証します。
-まさにそれ
匿名型は「魔法のように」Equals
とGetHashcode
を実装します
どこかに別のエラーがあると思います。大文字と小文字の区別?可変クラス?比較不可能なフィールド?
Distinct メソッドは、シーケンスから個別の要素を返します。
Reflectorを使用した実装を見ると、匿名型のDistinctIterator
が作成されていることがわかります。個別の反復子は、コレクションを列挙するときにSet
に要素を追加します。この列挙子は、Set
に既にあるすべての要素をスキップします。 Set
は、GetHashCode
およびEquals
メソッドを使用して、Set
に要素が既に存在するかどうかを定義します。
匿名型に対してどのようにGetHashCode
およびEquals
を実装しましたか? msdn で述べたように:
匿名型のEqualsおよびGetHashCodeメソッドは、プロパティのEqualsおよびGetHashcodeメソッドに関して定義されます。同じ匿名型の2つのインスタンスは、すべてのプロパティが等しい場合にのみ等しくなります。
そのため、個別のコレクションを反復処理する場合は、個別の匿名オブジェクトを確実に持つ必要があります。また、結果は、匿名型に使用するフィールドの数に依存しません。
以下のように、選択でKey
キーワードを使用すると機能します。
product.Select(m => new {Key m.CategoryId, Key m.CategoryName}).Distinct();
私はこれが古いスレッドを生み出していることを理解していますが、それは一部の人々を助けるかもしれないと考えました。通常、.NETを使用する場合はVB.NETでコーディングするため、Key
はC#に異なる方法で変換される場合があります。
これは私の解決策であり、異なるタイプのkeySelectorsをサポートしています:
public static IEnumerable<TSource> DistinctBy<TSource>(this IEnumerable<TSource> source, params Func<TSource, object>[] keySelectors)
{
// initialize the table
var seenKeysTable = keySelectors.ToDictionary(x => x, x => new HashSet<object>());
// loop through each element in source
foreach (var element in source)
{
// initialize the flag to true
var flag = true;
// loop through each keySelector a
foreach (var (keySelector, hashSet) in seenKeysTable)
{
// if all conditions are true
flag = flag && hashSet.Add(keySelector(element));
}
// if no duplicate key was added to table, then yield the list element
if (flag)
{
yield return element;
}
}
}
使用するには:
list.DistinctBy(d => d.CategoryId, d => d.CategoryName)
質問の見出し(ここで人々を惹きつけたもの)に答え、例が匿名型を使用していることを無視します。
このソリューションは、非匿名型でも機能します。匿名型には必要ありません。
ヘルパークラス:
/// <summary>
/// Allow IEqualityComparer to be configured within a lambda expression.
/// From https://stackoverflow.com/questions/98033/wrap-a-delegate-in-an-iequalitycomparer
/// </summary>
/// <typeparam name="T"></typeparam>
public class LambdaEqualityComparer<T> : IEqualityComparer<T>
{
readonly Func<T, T, bool> _comparer;
readonly Func<T, int> _hash;
/// <summary>
/// Simplest constructor, provide a conversion to string for type T to use as a comparison key (GetHashCode() and Equals().
/// https://stackoverflow.com/questions/98033/wrap-a-delegate-in-an-iequalitycomparer, user "orip"
/// </summary>
/// <param name="toString"></param>
public LambdaEqualityComparer(Func<T, string> toString)
: this((t1, t2) => toString(t1) == toString(t2), t => toString(t).GetHashCode())
{
}
/// <summary>
/// Constructor. Assumes T.GetHashCode() is accurate.
/// </summary>
/// <param name="comparer"></param>
public LambdaEqualityComparer(Func<T, T, bool> comparer)
: this(comparer, t => t.GetHashCode())
{
}
/// <summary>
/// Constructor, provide a equality comparer and a hash.
/// </summary>
/// <param name="comparer"></param>
/// <param name="hash"></param>
public LambdaEqualityComparer(Func<T, T, bool> comparer, Func<T, int> hash)
{
_comparer = comparer;
_hash = hash;
}
public bool Equals(T x, T y)
{
return _comparer(x, y);
}
public int GetHashCode(T obj)
{
return _hash(obj);
}
}
最も簡単な使用法:
List<Product> products = duplicatedProducts.Distinct(
new LambdaEqualityComparer<Product>(p =>
String.Format("{0}{1}{2}{3}",
p.ProductId,
p.ProductName,
p.CategoryId,
p.CategoryName))
).ToList();
最も単純な(それほど効率的ではない)使用法は、カスタムハッシュが回避されるように文字列表現にマップすることです。等しい文字列には既に等しいハッシュコードがあります。
public List<ItemCustom2> GetBrandListByCat(int id)
{
var OBJ = (from a in db.Items
join b in db.Brands on a.BrandId equals b.Id into abc1
where (a.ItemCategoryId == id)
from b in abc1.DefaultIfEmpty()
select new
{
ItemCategoryId = a.ItemCategoryId,
Brand_Name = b.Name,
Brand_Id = b.Id,
Brand_Pic = b.Pic,
}).Distinct();
List<ItemCustom2> ob = new List<ItemCustom2>();
foreach (var item in OBJ)
{
ItemCustom2 abc = new ItemCustom2();
abc.CategoryId = item.ItemCategoryId;
abc.BrandId = item.Brand_Id;
abc.BrandName = item.Brand_Name;
abc.BrandPic = item.Brand_Pic;
ob.Add(abc);
}
return ob;
}