C# linq 从列表的顶部和底部删除重复项并将重复项保留在中间
C# linq remove duplicates from the top and bottom of the list and keep the duplicates in the middle
C# linq 从列表的 top 和 bottom 中删除重复项,并将重复项保留在 middle
例如
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()
没有帮助,因为它也删除了 所有 中间的重复项。
**编辑:列表不会为空,无论顶部和底部是否重复,都应删除第一个和最后一个记录以计算业务逻辑。如果在顶部或底部发现重复项,也应将其删除。在执行删除操作之前,列表将按升序排列**
int topDuplicate = myList [0];
myList .RemoveAll(x => x == topDuplicate);
int bottomDuplicate = myList [myList .Count - 1];
myList .RemoveAll(x => x == bottomDuplicate);
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);
要删除列表开头的重复项,您可以使用 System.Linq 命名空间中的 .First()
和 .SkipWhile()
:
var firstDuplicate = myList.First();
var listWithoutFirstDuplicate = myList
.SkipWhile(l => l == firstDuplicate)
.ToList();
要删除列表 end 中的重复项,您将受益于 .Last()
和 .SkipLastWhile()
,had .SkipLastWhile()
存在:
var lastDuplicate = myList.Last();
var listWithoutLastDuplicate = myList
.SkipLastWhile(l => l == lastDuplicate)
.ToList();
Paulo Morgado 建议在 this blog post 中实施 .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;
}
}
}
Paulo 的 .SkipLastWhile()
实现根据谓词检查每个元素。如果某个元素的谓词 not 满足,则该元素是 returned。如果谓词 是 满足(即在您的场景中:如果元素等于 lastDuplicate
),则该元素不会立即 returned,而是添加到缓冲区。如果后续元素 不 满足谓词,则仅 return 编辑缓冲区的内容。
举几个例子:
{ 1, 9, 9, 9, 9 }.SkipLastWhile(i => i == 9)
将 return { 1 }
.
缓冲区将从 { 9 }
(索引 1 处的元素)到 { 9, 9, 9, 9 }
(从索引 1 到索引 4 的元素)构建,并且 而不是 returned.
{ 1, 9, 9, 9, 9, 1 }.SkipLastWhile(i => i == 1)
将 return { 1, 9, 9, 9, 9 }
.
缓冲区将首先是 { 1 }
(索引 0 处的元素),然后是 returned,并在找到 9
时清空(在索引 1 处)。当到达最后一个元素时,缓冲区将再次变为 { 1 }
;它不是 returned.
{ 9, 1, 9, 9, 9, 9 }.SkipLastWhile(i => i == 9)
将 return { 9, 1 }
.
缓冲区将首先是 { 9 }
(索引 0 处的元素),然后是 returned 并在找到 1
时清空(在索引 1 处)。当达到 9
(在索引 2 处)时,缓冲区再次开始建立,从 { 9 }
开始。在最后一个元素处,缓冲区将为 { 9, 9, 9, 9 }
,其内容为 not returned.
使用 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
正如Dmitry Bychenko所指出的,这个实现有两个问题:
- 如果
myList
为 null
或为空 ({ }
),则抛出异常
- none 的扩展方法可以容忍在
null
上被调用
.First()
(和.Last()
)在空列表上调用时抛出异常
- 如果第一个元素不重复(即不等于第二个元素),第一个元素将none排除;那不是预期的行为。类似地,最后一个元素也将被排除,即使它不是重复的。
解决这些问题的一种方法是在过滤掉实际重复项之前进行检查。如果用方法处理,可以这样实现:
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);
示例 fiddle here.
如果您只想从集合的顶部和底部删除重复项,但又想在中间保持相同值:
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
的帮助下 trim 集合:
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();
拜托,fiddle
C# linq 从列表的 top 和 bottom 中删除重复项,并将重复项保留在 middle
例如
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()
没有帮助,因为它也删除了 所有 中间的重复项。
**编辑:列表不会为空,无论顶部和底部是否重复,都应删除第一个和最后一个记录以计算业务逻辑。如果在顶部或底部发现重复项,也应将其删除。在执行删除操作之前,列表将按升序排列**
int topDuplicate = myList [0];
myList .RemoveAll(x => x == topDuplicate);
int bottomDuplicate = myList [myList .Count - 1];
myList .RemoveAll(x => x == bottomDuplicate);
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);
要删除列表开头的重复项,您可以使用 System.Linq 命名空间中的 .First()
和 .SkipWhile()
:
var firstDuplicate = myList.First();
var listWithoutFirstDuplicate = myList
.SkipWhile(l => l == firstDuplicate)
.ToList();
要删除列表 end 中的重复项,您将受益于 .Last()
和 .SkipLastWhile()
,had .SkipLastWhile()
存在:
var lastDuplicate = myList.Last();
var listWithoutLastDuplicate = myList
.SkipLastWhile(l => l == lastDuplicate)
.ToList();
Paulo Morgado 建议在 this blog post 中实施 .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;
}
}
}
Paulo 的 .SkipLastWhile()
实现根据谓词检查每个元素。如果某个元素的谓词 not 满足,则该元素是 returned。如果谓词 是 满足(即在您的场景中:如果元素等于 lastDuplicate
),则该元素不会立即 returned,而是添加到缓冲区。如果后续元素 不 满足谓词,则仅 return 编辑缓冲区的内容。
举几个例子:
{ 1, 9, 9, 9, 9 }.SkipLastWhile(i => i == 9)
将 return { 1 }
.
缓冲区将从 { 9 }
(索引 1 处的元素)到 { 9, 9, 9, 9 }
(从索引 1 到索引 4 的元素)构建,并且 而不是 returned.
{ 1, 9, 9, 9, 9, 1 }.SkipLastWhile(i => i == 1)
将 return { 1, 9, 9, 9, 9 }
.
缓冲区将首先是 { 1 }
(索引 0 处的元素),然后是 returned,并在找到 9
时清空(在索引 1 处)。当到达最后一个元素时,缓冲区将再次变为 { 1 }
;它不是 returned.
{ 9, 1, 9, 9, 9, 9 }.SkipLastWhile(i => i == 9)
将 return { 9, 1 }
.
缓冲区将首先是 { 9 }
(索引 0 处的元素),然后是 returned 并在找到 1
时清空(在索引 1 处)。当达到 9
(在索引 2 处)时,缓冲区再次开始建立,从 { 9 }
开始。在最后一个元素处,缓冲区将为 { 9, 9, 9, 9 }
,其内容为 not returned.
使用 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
正如Dmitry Bychenko所指出的,这个实现有两个问题:
- 如果
myList
为null
或为空 ({ }
),则抛出异常- none 的扩展方法可以容忍在
null
上被调用
.First()
(和.Last()
)在空列表上调用时抛出异常
- none 的扩展方法可以容忍在
- 如果第一个元素不重复(即不等于第二个元素),第一个元素将none排除;那不是预期的行为。类似地,最后一个元素也将被排除,即使它不是重复的。
解决这些问题的一种方法是在过滤掉实际重复项之前进行检查。如果用方法处理,可以这样实现:
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);
示例 fiddle here.
如果您只想从集合的顶部和底部删除重复项,但又想在中间保持相同值:
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
的帮助下 trim 集合:
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();
拜托,fiddle