我有一个元素数组,其中元素具有Flagged布尔值。
1 flagged
2 not flagged
3 not flagged
4 flagged
5 not flagged
6 not flagged
7 not flagged
8 flagged
9 not flagged
我想根据标记的指标将其分解为数组
输出>
array 1 {1,2,3}
array 2 {4,5,6,7}
array 3 {8,9}
答案 0 :(得分:7)
Linq没有这方面的操作符,但是我编写了一个你可以使用的扩展方法(在提交到MoreLinq的过程中,你也应该检查):
使用下面的操作符,您可以写:
var result =
items.Segment( (item,prevItem,idx) => item.Flagged )
.Select( seq => seq.ToArray() ) // converts each sequence to an array
.ToList();
这是扩展方法的代码:
public static IEnumerable<IEnumerable<T>> Segment<T>(IEnumerable<T> sequence, Func<T, T, int, bool> newSegmentIdentifier)
{
var index = -1;
using (var iter = sequence.GetEnumerator())
{
var segment = new List<T>();
var prevItem = default(T);
// ensure that the first item is always part
// of the first segment. This is an intentional
// behavior. Segmentation always begins with
// the second element in the sequence.
if (iter.MoveNext())
{
++index;
segment.Add(iter.Current);
prevItem = iter.Current;
}
while (iter.MoveNext())
{
++index;
// check if the item represents the start of a new segment
var isNewSegment = newSegmentIdentifier(iter.Current, prevItem, index);
prevItem = iter.Current;
if (!isNewSegment)
{
// if not a new segment, append and continue
segment.Add(iter.Current);
continue;
}
yield return segment; // yield the completed segment
// start a new segment...
segment = new List<T> { iter.Current };
}
// handle the case of the sequence ending before new segment is detected
if (segment.Count > 0)
yield return segment;
}
}
答案 1 :(得分:5)
我遇到了类似的问题,并使用GroupBy
和闭包来解决它。
//sample data
var arrayOfElements = new[] {
new { Id = 1, Flagged = true },
new { Id = 2, Flagged = false },
new { Id = 3, Flagged = false },
new { Id = 4, Flagged = true },
new { Id = 5, Flagged = false },
new { Id = 6, Flagged = false },
new { Id = 7, Flagged = false },
new { Id = 8, Flagged = true },
new { Id = 9, Flagged = false }
};
//this is the closure which will increase each time I see a flagged
int flagCounter = 0;
var query =
arrayOfElements.GroupBy(e =>
{
if (e.Flagged)
flagCounter++;
return flagCounter;
});
它的作用是对int(flagCounter
)进行分组,每次找到Flagged元素时都会增加。
请注意,这不适用于AsParallel()
。
测试结果:
foreach(var group in query)
{
Console.Write("\r\nGroup: ");
foreach (var element in group)
Console.Write(element.Id);
}
输出:
组:123
组:4567
组:89
答案 2 :(得分:2)
考虑到:
var arrayOfElements = new[] {
new { Id = 1, Flagged = true },
new { Id = 2, Flagged = false },
new { Id = 3, Flagged = false },
new { Id = 4, Flagged = true },
new { Id = 5, Flagged = false },
new { Id = 6, Flagged = false },
new { Id = 7, Flagged = false },
new { Id = 8, Flagged = true },
new { Id = 9, Flagged = false }
};
你可以写:
var grouped =
from i in arrayOfElements
where i.Flagged
select
(new[] { i.Id })
.Union(arrayOfElements.Where(i2 => i2.Id > i.Id).TakeWhile(i2 => !i2.Flagged).Select(i2 => i2.Id))
.ToArray();
如果您的元素按Id属性排序,则此方法有效。如果他们不这样做,你将不得不在原始阵列上注入一个序列,这也很容易用linq做,所以你会得到一个序列。
此外,更好的选择应该是:
// for each flagged element, slice the array,
// starting on the flagged element until the next flagged element
var grouped =
from i in arrayOfElements
where i.Flagged
select
arrayOfElements
.SkipWhile(i2 => i2 != i)
.TakeWhile(i2 => i2 == i || !i2.Flagged)
.Select(i2 => i2.Id)
.ToArray();
请注意,这些答案都是使用纯粹的linq。
答案 3 :(得分:1)
我不认为LINQ是完成此任务的正确工具。那怎么样:
public static List<List<T>> PartitionData<T>(T[] arr, Func<T, bool> flagSelector){
List<List<T>> output = new List<List<T>>();
List<T> partition = null;
bool first = true;
foreach(T obj in arr){
if(flagSelector(obj) || first){
partition = new List<T>();
output.Add(partition);
first = false;
}
partition.Add(obj);
}
return output;
}
一个小例子,来自FábioBatistas的数据发布:
var arrayOfElements = new[] {
new { Id = 1, Flagged = true },
new { Id = 2, Flagged = false },
new { Id = 3, Flagged = false },
new { Id = 4, Flagged = true },
new { Id = 5, Flagged = false },
new { Id = 6, Flagged = false },
new { Id = 7, Flagged = false },
new { Id = 8, Flagged = true },
new { Id = 9, Flagged = false }
};
var partitioned = PartitionData(arrayOfElements, x => x.Flagged);
答案 4 :(得分:0)
我认为LINQ不适合这个。可以使用Aggregate()来完成它,但我认为你最好只使用foreach()来构建结果。