[英]Removing items from an IEnumerable which match items in a List using LINQ
[英]Removing sequential repeating items from List<T> using linq
我正在尋找一種方法來防止重復列表中的項目,但仍保留訂單。 例如
1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4
應該成為
1, 2, 3, 4, 1, 2, 3, 4
我使用for
循環非常出色地完成了此任務,如下檢查下一項
public static List<T> RemoveSequencialRepeats<T>(List<T> input)
{
var result = new List<T>();
for (int index = 0; index < input.Count; index++)
{
if (index == input.Count - 1)
{
result.Add(input[index]);
}
else if (!input[index].Equals(input[index + 1]))
{
result.Add(input[index]);
}
}
return result;
}
是否有更優雅的方法(最好是使用LINQ)來做到這一點?
您可以創建擴展方法:
public static IEnumerable<T> RemoveSequentialRepeats<T>(
this IEnumerable<T> source)
{
using (var iterator = source.GetEnumerator())
{
var comparer = EqualityComparer<T>.Default;
if (!iterator.MoveNext())
yield break;
var current = iterator.Current;
yield return current;
while (iterator.MoveNext())
{
if (comparer.Equals(iterator.Current, current))
continue;
current = iterator.Current;
yield return current;
}
}
}
用法:
var result = items.RemoveSequentialRepeats().ToList();
您也可以使用純LINQ
:
List<int> list = new List<int>{1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4};
var result = list.Where((x, i) => i == 0 || x != list[i - 1]);
您可以編寫簡單的LINQ:
var l = new int[] { 1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 };
var k = new Nullable<int>();
var nl = l.Where(x => { var res = x != k; k = x; return res; }).ToArray();
int[8] { 1, 2, 3, 4, 1, 2, 3, 4 }
或pythonic(嗯,我最好的嘗試)方式:
l.Zip(l.Skip(1), (x, y) => new[] { x, y })
.Where(z => z[0] != z[1]).Select(a => a[0])
.Concat(new[] { l[l.Length - 1] }).ToArray()
int[8] { 1, 2, 3, 4, 1, 2, 3, 4 }
最簡單的( 編輯:還沒有看到它已經由King King建議)
l.Where((x, i) => i == l.Length - 1 || x != l[i + 1]).ToArray()
int[8] { 1, 2, 3, 4, 1, 2, 3, 4 }
如果您希望LINQ語句不依賴於調用內部結果的捕獲值,則將需要一些帶有聚合的構造,因為它是唯一帶有值和操作的方法。 即基於Zaheer Ahmed的代碼:
array.Aggregate(new List<string>(),
(items, element) =>
{
if (items.Count == 0 || items.Last() != element)
{
items.Add(element);
}
return items;
});
或者你甚至可以嘗試建立名單沒有if
:
array.Aggregate(Enumerable.Empty<string>(),
(items, element) => items.Concat(
Enumerable.Repeat(element,
items.Count() == 0 || items.Last() != element ? 1:0 ))
);
請注意,要使用Aggregate
獲得上述樣本的合理性能,您還需要攜帶last值( Last
必須在每個步驟上迭代整個序列),但是在Tuple
中攜帶3個值{IsEmpty, LastValue, Sequence}
代碼非常{IsEmpty, LastValue, Sequence}
看起來很奇怪。 這些樣本僅用於娛樂目的。
另一種選擇是對Zip
數組進行位移1並返回不相等的元素...
更實用的選擇是構建用於過濾值的迭代器:
IEnumerable<string> NonRepeated(IEnumerable<string> values)
{
string last = null;
bool lastSet = false;
foreach(var element in values)
{
if (!lastSet || last != element)
{
yield return element;
}
last = element;
lastSet = true;
}
}
如果您真的真的討厭這個世界,那么請使用LINQ:
var nmbs = new int[] { 1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4, 5 };
var res = nmbs
.Take(1)
.Concat(
nmbs.Skip(1)
.Zip(nmbs, (p, q) => new { prev = q, curr = p })
.Where(p => p.prev != p.curr)
.Select(p => p.curr));
但是請注意,您需要枚舉(至少部分地)可枚舉3次( Take
,的“左”的一部分Zip
,第一參數Zip
)。 此方法比建立或直接執行yield
方法要慢 。
說明:
.Take(1)
) .Skip(1)
並與所有的數字(配對.Zip(nmbs
),我們將調用curr
從最初的“集合”的數量和prev
號碼從第二“集合”(( (p, q) => new { prev = q, curr = p })
)。 然后,您僅獲取與前一個數字( .Where(p => p.prev != p.curr)
)不同的數字,然后從這些數字中獲取curr
值並丟棄prev
值( .Select(p => p.curr)
) .Concat(
這兩個集合( .Concat(
) 檢查新列表的最后一個和當前項目是否不相同,然后添加到新列表:
List<string> results = new List<string>();
results.Add(array.First());
foreach (var element in array)
{
if(results[results.Length - 1] != element)
results.Add(element);
}
或使用LINQ:
List<int> arr=new List<int>(){1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 };
List<int> result = new List<int>() { arr.First() };
arr.Select(x =>
{
if (result[result.Length - 1] != x) result.Add(x);
return x;
}).ToList();
對空對象進行正確的驗證。
嘗試這個:
class Program
{
static void Main(string[] args)
{
var input = "1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 ";
var list = input.Split(',').Select(i => i.Trim());
var result = list
.Select((s, i) =>
(s != list.Skip(i + 1).FirstOrDefault()) ? s : null)
.Where(s => s != null)
.ToList();
}
}
這是您需要的代碼:
public static List<int> RemoveSequencialRepeats(List<int> input)
{
var result = new List<int>();
result.Add(input.First());
result.AddRange(input.Where(p_element => result.Last() != p_element);
return result;
}
LINQ的魔力是:
result.Add(input.First());
result.AddRange(input.Where(p_element => result.Last() != p_element);
或者您可以創建如下擴展方法:
public static class Program
{
static void Main(string[] args)
{
List<int> numList=new List<int>(){1,2,2,2,4,5,3,2};
numList = numList.RemoveSequentialRepeats();
}
public static List<T> RemoveSequentialRepeats<T>(this List<T> p_input)
{
var result = new List<T> { p_input.First() };
result.AddRange(p_input.Where(p_element => !result.Last().Equals(p_element)));
return result;
}
}
如果您想引用F#項目,可以編寫
let rec dedupe = function
| x::y::rest when x = y -> x::dedupe rest
| x::rest -> x::dedupe rest
| _ -> []
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.