문제

C#에는 객체 목록이 있으며 목록 내에서 복제 된 것으로 간주되는 객체를 반환하는 방법이 필요합니다. 고유 한 결과 세트가 필요하지 않습니다. 저장소에서 삭제할 항목의 목록이 필요합니다.

이 예를 위해, "자동차"유형 목록이 있으며이 자동차 중 어느 자동차가 목록에서 다른 차와 동일한 색상인지 알아야한다고 가정 해 봅시다. 다음은 목록의 자동차와 해당 색상 속성입니다.

Car1.Color = Red;

Car2.Color = Blue;

Car3.Color = Green;

Car4.Color = Red;

Car5.Color = Red;

이 예를 들어 CAR4 및 CAR5를 포함하려면 결과 (iEenumerable <>, List <> 또는 무엇이든)가 필요하므로 저장소에 컬러 당 하나의 차량 만 있으면 저장소 또는 DB에서 삭제하려면 결과 (ienumerable <>, list <> 또는 무엇이든)가 필요합니다. 모든 도움이 감사하겠습니다.

도움이 되었습니까?

해결책

나는 어제 "투영에 의해 구별"을 쓰려고했을 때 우연히 이것을 코딩했다. 나는 포함했다! 내가 가져서는 안되지만 이번에는 맞습니다.

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;

이것은 자동차를 색상별로 그룹화 한 다음 각 그룹의 첫 번째 결과를 건너 뛰고 각 그룹에서 나머지를 단일 시퀀스로 반환합니다.

어떤 요구 사항을 보관하고 싶은지에 대한 특정 요구 사항이있는 경우, 예를 들어 자동차에 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<Car> GetDuplicateColors(List<Car> cars)
{
    return cars.Where(c => cars.Any(c2 => c2.Color == c.Color && cars.IndexOf(c2) < cars.IndexOf(c) ) );
}    

기본적으로 "목록에 동일한 색상과 더 작은 인덱스가있는 자동차가있는 자동차"를 의미합니다.

그러나 성능은 확실하지 않습니다. O (1) 중복에 대한 조회 (사전/해시 세트 방법과 같은)가 큰 세트의 경우 더 빠를 수 있다고 생각합니다.

새로운 것을 만듭니다 Dictionary<Color, Car> foundColors 그리고 a 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> 생성 a Dictionary<T, int>
  • 당신을 통해 반복하십시오 Dictionary<T, int> 위치 삭제 int > 1입니다

무엇이든 남은 것 Dictionary 복제물이 있습니다. 실제로 삭제하는 두 번째 부분은 물론 선택 사항입니다. 당신은 그냥 반복 할 수 있습니다 Dictionary 그리고 행동을 취할> 1을 찾으십시오.

편집 : 알겠습니다. Ryan이 실제로 코드를 주었기 때문에 부딪쳤습니다. ;)

내 대답은 Joe Coehoorn, Greg Beech 및 Jon Skeet과 같은 추종자 응답자로부터 영감을 얻습니다.

나는 당신이 정적 자동차 색상 목록을 가지고 있다는 (실제 단어 효율성)라는 가정과 함께 전체 예를 제공하기로 결정했습니다. 다음 코드는 우아한 방식으로 우아한 문제에 대한 완전한 솔루션을 보여줍니다.

#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

나는 나중에 다시 돌아와서이 솔루션을 세 가지 영감과 비교할 것입니다. 다소 흥미 롭습니다.

공개 정적 iqueryable 복제 (이 ienumerable 소스) 여기서 tsource : icompary {

if (source == null)   
     throw new ArgumentNullException("source");   
 return source.Where(x => source.Count(y=>y.Equals(x)) > 1).AsQueryable<TSource>();   

}

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top