我有这堂课
public enum SubStatus { active, inactive, unset}
public class SubItem
{
public SubStatus Status { get; set; }
public string Value { get; set; }
}
该类中的值可能看起来像这样
List<SubItem> list = new List<SubItem>()
{
new SubItem() { Status = SubStatus.active, Value = "1" },
new SubItem() { Status = SubStatus.active, Value = "2" }, //active again, add "2" to the previous
new SubItem() { Status = SubStatus.inactive, Value = "1" },
new SubItem() { Status = SubStatus.active, Value = "2" },
new SubItem() { Status = SubStatus.unset, Value = "2" },
new SubItem() { Status = SubStatus.unset, Value = "1" }, //unset again, add "1" to the previous
new SubItem() { Status = SubStatus.unset, Value = "1" } //unset again, add "1" to the previous
};
现在,我需要将字符串值合并在一起,以防某个项目的Status
等于上一个项目的Status
。所以在这个例子中,结果应该像这样
List <SubItem> output = new List<SubItem>()
{
new SubItem() { Status = SubStatus.active, Value = "12" },
new SubItem() { Status = SubStatus.inactive, Value = "1" },
new SubItem() { Status = SubStatus.active, Value = "2" },
new SubItem() { Status = SubStatus.unset, Value = "211" }
};
到目前为止我尝试过的方法都可以奏效,但是以某种方式我认为这种方法是错误的,并且使用linq会有更好的方法...
List<SubItem> result = new List<SubItem>();
SubItem temp = list.FirstOrDefault();
if (temp != null)
{
foreach (SubItem item in list.Skip(1))
{
if (temp.Status == item.Status)
{
temp.Value += item.Value;
}
else if (temp.Status != item.Status)
{
result.Add(temp);
temp = item;
}
}
}
result.Add(temp);
答案 0 :(得分:3)
标准Linq 不提供分组,但我们可以手动实现:
public static partial class EnumerableExtensions {
public static IEnumerable<T[]> ToBatch<T>(this IEnumerable<T> source,
Func<ICollection<T>, T, bool> addToBatch) {
if (null == source)
throw new ArgumentNullException(nameof(source));
else if (null == addToBatch)
throw new ArgumentNullException(nameof(addToBatch));
List<T> batch = new List<T>();
foreach (T item in source) {
// Shall we start a new batch?
if (batch.Count > 0 && !addToBatch(batch, item)) {
yield return batch.ToArray();
batch.Clear();
}
batch.Add(item);
}
if (batch.Count > 0) // do we have a last batch?
yield return batch.ToArray();
}
}
现在,我们可以将实现用作
// Test Data
List<SubItem> list = new List<SubItem>() {
new SubItem() { Status = SubStatus.active, Value = "1" },
new SubItem() { Status = SubStatus.active, Value = "2" },
new SubItem() { Status = SubStatus.inactive, Value = "1" },
new SubItem() { Status = SubStatus.active, Value = "2" },
new SubItem() { Status = SubStatus.unset, Value = "2" },
new SubItem() { Status = SubStatus.unset, Value = "1" },
new SubItem() { Status = SubStatus.unset, Value = "1" }
};
...
List<SubItem> result = list
.ToBatch((batch, item) => item.Status == batch.First().Status)
.Select(batch => new SubItem() {
Status = batch.First().Status,
Value = string.Concat(batch.Select(item => item.Value))
})
.ToList();
// Let's have a look
Console.Write(string.Join(Environment.NewLine, result
.Select(item => $"{item.Status,-8} : {item.Value}")));
结果:
active : 12
inactive : 1
active : 2
unset : 211
答案 1 :(得分:2)
您可以使用Aggregate
,但IMO并不比常规foreach
好:
var result = list
.Aggregate(
(IEnumerable<SubItem>)Array.Empty<SubItem>(),
(result, item) =>
result.LastOrDefault()?.Status == item.Status
? result.SkipLast(1).Concat(new [] { new SubItem { Status = item.Status, Value = result.Last().Value + item.Value } })
: result.Concat(new [] { item })
)
.ToList();
这是另一种想法(使用TakeWhile
),但更为复杂:
var result2 = list
.Select((item, index) => list
.Take(index + 1)
.Reverse()
.TakeWhile(x => x.Status == item.Status)
.Select((x, i) => Tuple.Create(x, index - i))
.Reverse()
)
.GroupBy(x => x.Select(y => y.Item2).Min())
.Select(x => x.Last())
.Select(x => new SubItem
{
Status = x.First().Item1.Status,
Value = string.Join("", x.Select(y => y.Item1.Value))
})
.ToList();
答案 2 :(得分:1)
您不能使用标准LINQ来完成全部操作,但是可以利用GroupAdjacent
中的MoreLINQ
:
var result = list
.GroupAdjacent(item => item.Status)
.Select(grp => new SubItem
{
Status = grp.Key,
Value = string.Concat(grp.Select(item => item.Value))
});
foreach (var item in result)
{
Console.WriteLine(item.ToString());
}
通过Status
对相邻项进行分组,使用Enumerable.Select
选择新的SubItem
,然后使用String.Concat
组合分组的值。
上面的示例输出以下内容:
Status=active,Value=12
Status=inactive,Value=1
Status=active,Value=2
Status=unset,Value=211
注意:以上内容覆盖了ToString()
的{{1}}方法:
SubItem