如果我有一些收集,例如:整數的集合:集團收集的序貫要素平等
1 1 2 2 3 3 1 2 3 4 4 1 1 2 3
,我需要組只序貫元素,在這個例子:
1 1
2 2
3 3
1
2
3
4 4
1 1
2
3
我構建了LINQ表達式,該表達式創建一個匿名集合,它具有集合中元素的索引,它是平等標記,然後我將集合與它向前移動一個位置,然後對元素進行分組。但是我認爲這個算法太多了。
是否有更優雅的解決方案?
如果我有一些收集,例如:整數的集合:集團收集的序貫要素平等
1 1 2 2 3 3 1 2 3 4 4 1 1 2 3
,我需要組只序貫元素,在這個例子:
1 1
2 2
3 3
1
2
3
4 4
1 1
2
3
我構建了LINQ表達式,該表達式創建一個匿名集合,它具有集合中元素的索引,它是平等標記,然後我將集合與它向前移動一個位置,然後對元素進行分組。但是我認爲這個算法太多了。
是否有更優雅的解決方案?
這裏實現,它使用LINQ:
//Input sequence
int[] input = new int[] { 1, 1, 2, 2, 3, 3, 1, 2, 3, 4, 4, 1, 1, 2, 3 };
//Group number
int i = 0;
//Result array [<group number>][]
int[][] values =
//Select new anonymous object, which contains the source value from input and its group number
input.Select((item, index) => new { Key = index > 0 ? (item == input[index - 1] ? i : ++i) : 0, Value = item })
//Group anonymous objects by group number
.GroupBy(pair => pair.Key)
//Select values for each group
.Select(g => g.Select(x => x.Value).ToArray())
.ToArray();
我試圖找到允許未初始化集合的方法,但是,正如我所看到的,在此任務中不可能。所以,感謝這樣簡潔的方式 – dotFive
好吧,如果你真的不需要LINQ - 只是簡單的循環將做的工作:
var res = new List<List<int>>();
foreach(int i in data)
{
var c = res.Count;
if (c == 0 || res[c - 1][0] != i)
res.Add(new List<int>() { i });
else
res[c - 1].Add(i);
}
您也可以使用外部變量LINQ,但是這可能會更難閱讀
編輯:我發現了一個Linq的方式來使用Aggregate擴展方法來做到這一點。此方法允許您使用Func lambda中的累加器參數查看集合中的以前項目。那就是:
static List<List<int>> Group2(List<int> data)
{
return data.Aggregate(new List<List<int>>(), (list, item) =>
{
if (list.Count == 0 || list[list.Count - 1][0] != item)
{
list.Add(new List<int> { item });
}
else
{
list[list.Count - 1].Add(item);
}
return list;
});
}
我想不出一個LINQ的方式來做到這一點,因爲我不認爲它的任何方法讓你看看這種方式以前的項目。這裏有一個通用的方法我會做到這一點:
static IEnumerable<List<T>> Group<T>(IEnumerable<T> list, IEqualityComparer<T> comp)
{
T previous = default(T);
bool previousExists = false;
var eee = list.GetEnumerator();
List<T> result = null;
while(eee.MoveNext())
{
T current = eee.Current;
if (previousExists && comp.Equals(current, previous))
{
result.Add(current);
}
else
{
if (result != null)
yield return result;
result = new List<T> { current };
}
previous = current;
previousExists = true;
}
if (result != null)
yield return result;
}
的問題是,LINQ缺少Select
方法,可以讓你挖掘到先前的結果。
如果那樣,你只需要編寫這樣的事:
var sequences = items
.SelectWithPreviousResult(
new { Item = -1, GroupNumber = 0 }, // default result (used for first item)
(item, previous) => new
{
Item = item,
GroupNumber = previous.Item == x
? previous.GroupNumber
: previous.GroupNumber + 1 })
.GroupBy(x => x.GroupNumber, x => x.Item);
這樣做有什麼用GroupNumber
從0開始,只有噹噹前項目不同於增量沿着選擇當前項目前一個項目。然後它按GroupNumber
進行分組,並將組員簡化爲只是項目。
當然,以上不會編譯,因爲SelectWithPrevious
尚不存在。不過,關於LINQ的好處是你可以很容易地編寫你自己的擴展方法。該SelectWithPreviousResult
方法可以實現這樣的:
public static class LinqExtensions
{
public static IEnumerable<TResult> SelectWithPreviousResult<TSource, TResult>(
this IEnumerable<TSource> items,
TResult defaultResult,
Func<TSource, TResult, TResult> func)
{
var previousResult = defaultResult;
foreach (var item in items)
{
var result = func(item, previousResult);
previousResult = result;
yield return result;
}
}
}
優點這種方式來實現它的是,你可以重複使用擴展方法來解決類似的問題,和你的代碼獲得了一些可讀性與使用純環或一個複雜的Aggregate
表達。
謝謝,使用這種擴展方法會很有趣 – dotFive
你能展示代碼嗎? –
如果連續有兩個以上相同的元素,算法是否工作?它會把「1 1 1 2」分成「1 1 1」和「2」嗎?或者將它分成「1 1」,「1 1」,「2」? – user2023861
@YacoubMassad http:// pastebin。com/ahGsi4AV – dotFive