С# linq удаляет дубликаты из верхней и нижней части списка и сохраняет дубликаты в середине

С# linq удаляет дубликаты из верхняя и низ списка и сохраняет дубликаты в середина

Например,

  var myArray = new[] { 
    1, 1, 2, 2, 3, 4, 5, 5, 9, 9 
  };
  
  List<int> myList = myArray.ToList();

Ожидаемый результат после удаления дубликатов вверху и внизу списка

{ 2, 2, 3, 4, 5, 5 };

Пожалуйста, посоветуйте, как выполнить эту логику, и попытка myList.Distinct() не поможет, так как она также удаляет все дубликаты в середине.

** Обновлено: список не будет ПУСТОЙ, и независимо от дубликатов вверху и внизу первая и последняя записи должны быть удалены для расчета бизнес-логики. Если дубликаты найдены вверху или внизу, их также следует удалить. Список будет упорядочен по возрастанию перед выполнением операции удаления **

Если у вас есть одна или несколько единиц или девяток в середине, вы хотите сохранить или удалить их? Кроме того, что, если первый/последний элемент нет является дубликатом; вы хотите сохранить или удалить это?

41686d6564 stands w. Palestine 15.05.2022 07:38

@ 41686d6564standsw.Палестина Обновил вопрос

All_Languages 15.05.2022 14:37

Это спецификация, а не вопрос. Где у вас возникли проблемы с программированием?

Gert Arnold 15.05.2022 22:21
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
84
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

    int topDuplicate = myList [0];
    myList .RemoveAll(x => x == topDuplicate);
    int bottomDuplicate = myList [myList .Count - 1];
    myList .RemoveAll(x => x == bottomDuplicate);

1. Ваш код выдает исключение на пустой список. 2. Ваш код удаляет первый и последний элементы, даже если они не дублируются, например. {1, 2, 3}: фактически {2} когда {1, 2, 3} ожидается.

Dmitry Bychenko 15.05.2022 09:23
Ответ принят как подходящий
var myList = new List<int> { 1, 1, 2, 2, 3, 4, 5, 5, 9, 9 };
    var topDublicate = myList.First();
    var lastDublicate = myList.Last();

    myList.RemoveAll(l => l == topDublicate);
    myList.RemoveAll(l => l == lastDublicate);

1. Ваш код выдает исключение на пустой список. 2. Ваш код удаляет первый и последний элементы, даже если они не дублируются, например. {1, 2, 3}: фактически {2} когда {1, 2, 3} ожидается.

Dmitry Bychenko 15.05.2022 09:23

Для удаления дубликатов в начале списка вы можете использовать .First() и .SkipWhile() в пространстве имен System.Linq:

var firstDuplicate = myList.First();

var listWithoutFirstDuplicate = myList
    .SkipWhile(l => l == firstDuplicate)
    .ToList();

Для удаления дубликатов в конец списка вам пригодятся .Last() и .SkipLastWhile(), существовал .SkipLastWhile():

var lastDuplicate = myList.Last();

var listWithoutLastDuplicate = myList
    .SkipLastWhile(l => l == lastDuplicate)
    .ToList();

Пауло Моргадо предложил реализовать .SkipLastWhile() в этот пост в блоге. Это выглядит так:

public static IEnumerable<TSource> SkipLastWhile<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    var buffer = new List<TSource>();

    foreach (var item in source)
    {
        if (predicate(item))
        {
            buffer.Add(item);
        }
        else
        {
            if (buffer.Count > 0)
            {
                foreach (var bufferedItem in buffer)
                {
                    yield return bufferedItem;
                }

                buffer.Clear();
            }

            yield return item;
        }
    }
}

Реализация Пауло .SkipLastWhile() проверяет каждый элемент на соответствие предикату. Если для элемента выполнен предикат нет, элемент возвращается. Если предикат является выполнен (т.е. в вашем сценарии: если элемент равен lastDuplicate), элемент не возвращается сразу, а добавляется в буфер. Содержимое буфера возвращается только в том случае, если последующий элемент нет удовлетворяет предикату.


Несколько примеров:

{ 1, 9, 9, 9, 9 }.SkipLastWhile(i => i == 9)

вернется { 1 }.
Буфер будет увеличиваться от { 9 } (элемент с индексом 1) до { 9, 9, 9, 9 } (элементы с индексом 1 по индексу 4), и будет возвращаться нет.

{ 1, 9, 9, 9, 9, 1 }.SkipLastWhile(i => i == 1)

вернется { 1, 9, 9, 9, 9 }.
Буфер сначала будет { 1 } (элемент с индексом 0), затем будет возвращен и очищен, когда будет найден 9 (с индексом 1). При достижении последнего элемента буфер снова будет { 1 }; и не возвращается.

{ 9, 1, 9, 9, 9, 9 }.SkipLastWhile(i => i == 9)

вернется { 9, 1 }.
Буфер сначала будет { 9 } (элемент с индексом 0), затем будет возвращен и очищен, когда будет найден 1 (с индексом 1). При достижении 9 (по индексу 2) буфер снова начинает накапливаться, начиная с { 9 }. В последнем элементе буфер будет { 9, 9, 9, 9 }, а его содержимое будет возвращено нет.


Используя эту реализацию SkipLastWhile(), вы можете получить отфильтрованный список:

var myList = new List<int> { 1, 1, 9, 2, 2, 3, 9, 4, 5, 5, 1, 9, 9 };

var firstDuplicate = myList.First();
var lastDuplicate = myList.Last();

var myFilteredList = myList
    .SkipWhile(l => l == firstDuplicate)
    .SkipLastWhile(l => l == lastDuplicate)
    .ToList();

Вывод для заданного myList следующий:

9, 2, 2, 3, 9, 4, 5, 5, 1


Как указал Дмитрий Быченко, в этой реализации есть две проблемы:

  1. Если myList равно null или пусто ({ }), возникает исключение
    • ни один из методов расширения не может допустить вызова null
    • .First().Last()) генерировать исключение при вызове из пустого списка
  2. Если первый элемент не дублируется (т. е. не равен второму элементу), первый элемент тем не менее будет исключен; это не ожидаемое поведение. Точно так же последний элемент также будет исключен, даже если он не является дубликатом.

Один из способов решения этих проблем — выполнять проверки перед тем, как отфильтровать фактические дубликаты. Если это обрабатывается в методе, это может быть реализовано следующим образом:

public static List<int> GetFilteredList(List<int> list)
{
    // if list is null; return null
    if (list == null)
    {
        return null;
    }
    
    // If list is empty or contains only one element; return list as new list
    if (!list.Skip(1).Any())
    {
        return list.ToList();
    }
    
    var filtered = list.AsEnumerable();
    
    // Remove duplicates at beginning of list (if any)
    if (list.First() == list.Skip(1).First())
    {
        filtered = filtered.SkipWhile(l => l == list.First());
    }
    
    // Remove duplicates at end of list (if any)
    if (list.Last() == list.SkipLast(1).Last())
    {
        filtered = filtered.SkipLastWhile(l => l == list.Last());
    }
    
    return filtered.ToList();
}

Его можно назвать следующим образом:

var filteredList = GetFilteredList(myList);

Пример скрипки здесь.

Ваш последний код выдает исключение на пустой список (var myList = new List<int> {};) и отсекает первый и последний элементы, даже если они не дублирует: (var myList = new List<int> {1, 2, 3, 4, 5};, here I expected 1, 2, 3, 4, 5`, но получил 2, 3, 4

Dmitry Bychenko 15.05.2022 08:43

@DmitryBychenko Вы абсолютно правы; спасибо, что указали на это.

Astrid E. 15.05.2022 09:05

Обновлено (устранение проблем и добавление предлагаемой реализации для их решения).

Astrid E. 15.05.2022 10:06

Если вы хотите удалить дубликаты только из самого верха и низа коллекции, но хотите сохранить значения такой же посередине:

1, 1, 2, 3, 1, 1, 8, 8, 9, 9, 4, 5, 9, 9 => 2, 3, 1, 1, 8, 8, 9, 9, 4, 5
                                                  ^  ^     ^  ^
                                                    preserved

вы можете вычислить границы left и right, а затем обрезать коллекцию с помощью Skip и Take:

      int[] myArray = new int[] { 
        ... 
      };

      ...

      int left = 0;

      for (int i = 1; i < myArray.Length; ++i)
        if (myArray[i - 1] == myArray[i])
          left = i + 1;
        else
          break;

      int right = myArray.Length - 1;

      for (int i = myArray.Length - 2; i >= 0; --i)
        if (myArray[i + 1] == myArray[i])
          right = i - 1;
        else
          break;

      List<int> myList = myArray
        .Skip(left)
        .Take(right - left + 1)
        .ToList();

Если вы хотите удалить все значения, которые оказались дубликатами

1, 1, 2, 3, 1, 1, 8, 8, 9, 9, 4, 5, 9, 9 => 2, 3, 8, 8, 4, 5

вы можете собрать эти значения, а затем отфильтровать:

      int[] myArray = new int[] { 
        ... 
      };

      ...

      HashSet<int> remove = new HashSet<int>();

      if (myArray.Length > 1) {
        if (myArray[0] == myArray[1])
          remove.Add(myArray[0]);
        if (myArray[myArray.Length - 1] == myArray[myArray.Length - 2])
          remove.Add(myArray[myArray.Length - 1]);
      }

      var myList = myArray
        .Where(item => !remove.Contains(item))
        .ToList();

Пожалуйста, рабочий пример

Другие вопросы по теме