首页 > 解决方案 > 基于另一个属性连接一个属性的值

问题描述

我有这门课

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);   

标签: c#arraysstringlistlinq

解决方案


标准 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

推荐阅读