C# 3.0:List<> から重複を返す必要があります
-
20-08-2019 - |
質問
C# にオブジェクトの List<> があり、リスト内で重複とみなされるオブジェクトを返す方法が必要です。Distinct 結果セットは必要ありません。リポジトリから削除するアイテムのリストが必要です。
この例では、「車」タイプのリストがあり、これらの車のどれがリスト内の別の車と同じ色であるかを知る必要があるとします。リスト内の車とその色のプロパティは次のとおりです。
Car1.Color = Red;
Car2.Color = Blue;
Car3.Color = Green;
Car4.Color = Red;
Car5.Color = Red;
この例では、結果 (IEnumerable<>、List<> など) に Car4 と Car5 を含める必要があります。これは、これらをリポジトリまたはデータベースから削除して、リポジトリ内に色ごとに 1 台の車だけが存在するようにしたいためです。助けていただければ幸いです。
解決
私は不注意I「が投影によって異なる」を書き込もうとしたときに、昨日、この符号化されました。私は含まれて!私は持っていないはずですが、今回はそれがちょうどいいですときます:
public static IEnumerable<TSource> DuplicatesBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> seenKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
// Yield it if the key hasn't actually been added - i.e. it
// was already in the set
if (!seenKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
あなたは、その後でそれを呼び出すと思います:
var duplicates = cars.DuplicatesBy(car => car.Color);
他のヒント
var duplicates = from car in cars
group car by car.Color into grouped
from car in grouped.Skip(1)
select car;
次に、この色によって車グループと、各グループが単一のシーケンスに平坦化から余りを返す、各グループからの最初の結果をスキップします。
あなたは、例えばを維持したい1についての特定の要件を持っている場合車はId
性質を持っており、あなたは最低Id
で車を維持したい場合は、例えば、そこにいくつかの順序を追加することができます。
var duplicates = from car in cars
group car by car.Color into grouped
from car in grouped.OrderBy(c => c.Id).Skip(1)
select car;
ここで私はあなたが何をしようとして、それがより明確になり考える若干異なるLINQのソリューションです
var s = from car in cars
group car by car.Color into g
where g.Count() == 1
select g.First();
それはちょうど、色で車をグループ化する複数の要素を持っているすべてのグループを投げ、その後、戻ってIEnumerableに残りを入れている。
IEnumerable<Car> GetDuplicateColors(List<Car> cars)
{
return cars.Where(c => cars.Any(c2 => c2.Color == c.Color && cars.IndexOf(c2) < cars.IndexOf(c) ) );
}
これは基本的に意味します。
「同じ色で、リスト内のすべての車と小さいインデックスがあります車を返します」しかし、パフォーマンスのわかりません。 I(1)(辞書/ HashSetの方法のように)複製するためのルックアップが大きいセットに速くすることができるOとアプローチがあると思われる。
新しいDictionary<Color, Car> foundColors
とList<Car> carsToDelete
を作成します。
次に、あなたはそうのような車のあなたの元のリストを繰り返し処理します
foreach(Car c in listOfCars)
{
if (foundColors.containsKey(c.Color))
{
carsToDelete.Add(c);
}
else
{
foundColors.Add(c.Color, c);
}
}
そして、あなたはfoundColorsにありますすべての車を削除することができます。
あなたはif
文で、あなたの「レコードを削除」のロジックを置く代わりに、新しいリストを作成することによって、マイナーなパフォーマンスの向上を得ることができるが、あなたが質問を言葉で表現方法は、あなたがリストにそれらを収集するために必要なことが示唆されました。
実際にコーディングせずに、次のようなアルゴリズムはどうでしょうか。
- を繰り返します
List<T>
を作成するDictionary<T, int>
- を繰り返します
Dictionary<T, int>
エントリを削除します。int
>1です
中に残ったものは何でも、 Dictionary
重複があります。実際に削除する 2 番目の部分は、もちろんオプションです。を繰り返すだけで済みます Dictionary
そしてアクションを起こすために >1 を探します。
編集:OK、ライアンが実際にコードをくれたので、私はライアンのものを上げました。;)
私の答えは、フォロワーの回答者から(この順序で)インスピレーションを取ります。ジョーCoehoorn、グレッグブナとジョンスキート
私はあなたの車の色の静的なリストを持っている(実際の単語効率のため)であることを前提に、完全な例を提供することを決めました。私は、次のコードはエレガントで問題に対する完全な解決策を示しているが、必ずしも超効率的な方法を信じます。
#region SearchForNonDistinctMembersInAGenericListSample
public static string[] carColors = new[]{"Red", "Blue", "Green"};
public static string[] carStyles = new[]{"Compact", "Sedan", "SUV", "Mini-Van", "Jeep"};
public class Car
{
public Car(){}
public string Color { get; set; }
public string Style { get; set; }
}
public static List<Car> SearchForNonDistinctMembersInAList()
{
// pass in cars normally, but declare here for brevity
var cars = new List<Car>(5) { new Car(){Color=carColors[0], Style=carStyles[0]},
new Car(){Color=carColors[1],Style=carStyles[1]},
new Car(){Color=carColors[0],Style=carStyles[2]},
new Car(){Color=carColors[2],Style=carStyles[3]},
new Car(){Color=carColors[0],Style=carStyles[4]}};
List<Car> carDupes = new List<Car>();
for (int i = 0; i < carColors.Length; i++)
{
Func<Car,bool> dupeMatcher = c => c.Color == carColors[i];
int count = cars.Count<Car>(dupeMatcher);
if (count > 1) // we have duplicates
{
foreach (Car dupe in cars.Where<Car>(dupeMatcher).Skip<Car>(1))
{
carDupes.Add(dupe);
}
}
}
return carDupes;
}
#endregion
私はちょうどスタイルを対比し、後でここに戻ってきて、そのインスピレーションの3つのすべてにこのソリューションを比較するつもりです。それはかなり面白いです。
のpublic staticのIQueryable重複(このIEnumerableをソース)TSOURCE:IComparableを {
if (source == null)
throw new ArgumentNullException("source");
return source.Where(x => source.Count(y=>y.Equals(x)) > 1).AsQueryable<TSource>();
}