【问题标题】:C# Combine Two Lists With Overlapping DataC# 将两个列表与重叠数据合并
【发布时间】:2016-03-11 01:11:50
【问题描述】:

如果我有 2 个字符串列表

List<string> history = new List<string>(){ "AA", "BB", "CC", "AA" };
List<string> potentialNew = new List<string>(){ "CC", "AA", "DD", "EE", "FF", "AA"};

我需要一种方法来组合列表,同时防止“重叠”并保持相同的顺序。因此,在上面的示例中,将有一个组合列表:

AA, BB, CC, AA, DD, EE, FF, AA

换句话说,只有 DD、EE、FF 和 AA 被添加到 history 列表中。

我这几天一直在尝试解决这个问题,无数次搜索都没有找到解决方案。任何帮助将不胜感激!

【问题讨论】:

  • 如果"BB" 出现在第二个列表中的"FF", "AA" 之后,您的预期输出是什么?
  • 如果第二个列表是 CC, AA, DD, EE, FF, AA, BB 预期的输出将是 AA, BB, CC, AA, DD, EE, FF, AA, BB跨度>
  • List1 "AA", "BB", "CC", "AA" 和 list2 "CC", "AA", "DD", "EE", "FF", "AA","BB"
  • 组合列表将是“AA”、“BB”、“CC”、“AA”、“DD”、“EE”、“FF”、“AA”、“BB”跨度>
  • 所以坦率地说;从第二个列表中删除项目背后的逻辑是什么?

标签: c# .net linq list overlapping


【解决方案1】:

这将为您在问题中提到的给定输入集提供预期的输出:

 List<string> history = new List<string>() { "AA", "BB", "CC", "AA" };
 List<string> potentialNew = new List<string>() { "CC", "AA", "DD", "EE", "FF" };
 var result = history.Concat(potentialNew.Where(x => !history.Contains(x)).ToList());

.Concat() 方法允许您连接两个列表。我们正在从potentialNew 中提取第一个列表中不存在的特定项目,并将它们与第一个列表连接。

更新:根据我们的讨论,我得出的结论是您正在寻找类似以下内容:

string lastItem = history.Last();
   int lastIndexToCheck=history.Count-2,i=0;
   for (; i < potentialNew.Count - 1; i++)
       {
          if (potentialNew[i] == lastItem && potentialNew[i - 1] == history[lastIndexToCheck])
              {
                 break;
              }
       }
       history.AddRange(potentialNew.Skip(i+1).ToList());  

现在历史将包含所需的元素集。

【讨论】:

  • 我应该在我的问题中提到它,但是假设我在第二个列表中的“FF”之后再次有“AA”。然后还需要将“AA”添加到新列表中。 (我已经编辑了我的问题以反映这一点)
  • 这种组合列表的应用是游戏的聊天记录。经常使用 OCR 解析的聊天日志,我正在保留聊天条目的运行日志,因此我需要将我已经记录的内容与新的聊天条目进行比较。
  • 似乎不是做聊天记录的好方法。在收到条目时附加条目不是更好吗?使用异步?使用这种设计,随着您的聊天日志的增长,您的应用程序总是会变慢。
  • 游戏是一个不同的应用程序(我没有制作),否则我会这样做。无论如何,我只需要与最后 20 个左右的条目进行比较,因为我只是在检查新值。对吗?
【解决方案2】:
using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        List<string> history = new List<string>(){ "AA", "BB", "CC", "AA" };
        List<string> potentialNew = new List<string>(){ "CC", "AA", "DD", "EE", "FF" };
        // make lists equal length

        foreach(var x in history.ConcatOverlap(potentialNew)){
            Console.WriteLine(x);
        }
    }


}

public static class Ext{
    public static IEnumerable<string> ConcatOverlap(this List<string> history, List<string> potentialNew){
        var hleng = history.Count();
        var pleng = potentialNew.Count();
        if(pleng > hleng) history = history.Concat(Enumerable.Range(1, pleng - hleng).Select(x => string.Empty)).ToList();
        if(hleng > pleng) potentialNew = Enumerable.Range(1, hleng - pleng).Select(x => string.Empty).Concat(potentialNew).ToList();


        var zipped = history.Zip(potentialNew, (a,b)=> new {First=a,Next=b, Equal = (a.Equals(b) || string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b))});
        var count = 0;
        var max = pleng > hleng ? pleng : hleng;
        Console.WriteLine("Max " + max);
        while(zipped.Any(x => !x.Equal) && count < max - 1){
            count++;
            potentialNew.Insert(0,string.Empty);
            history.Add(string.Empty);
            zipped = history.Zip(potentialNew, (a,b)=> new {First=a,Next=b, Equal = (a.Equals(b) || string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b))});
        }
        return zipped.Select(x => string.IsNullOrEmpty(x.First) ? x.Next : x.First);
    }
}

再考虑一下:

public static IEnumerable<T> ConcatOverlap<T>(this IEnumerable<T> head, IEnumerable<T> tail){

    var skip = 0;
    var hLen = head.Count();
    while(head.Skip(skip).Zip(tail, (a,b) => a.Equals(b)).Any(x => !x) && skip < hLen){
        skip++;
    }

    return head.Take(skip).Concat(tail);
}

【讨论】:

    【解决方案3】:
    var history = new List<string>() { "AA", "BB", "CC", "AA" };
    var potentialNew = new List<string>() { "CC", "AA", "DD", "EE", "FF" };
    
    // Get the min. number of items to compare that 2 lists
    for (int count = Math.Min(history.Count(), potentialNew.Count()); count >= 0; count--)
    {
        // Get the items from the back of history list, and get the items from potential list
        // Compare them by SequenceEqual()
        if (history.Skip(history.Count() - count).Take(count).SequenceEqual(potentialNew.Take(count)))
        {
            // Add the items to the result if found. It must be the greatest common part
            return history.Concat(potentialNew.Skip(count));
        }
    }
    

    .Net Fiddle

    【讨论】:

      【解决方案4】:

      这看起来很简单:

      List<string> history = new List<string>(){ "AA", "BB", "CC", "AA" };
      List<string> potentialNew = new List<string>(){ "CC", "AA", "DD", "EE", "FF", "AA"};
      
      potentialNew.Aggregate(history, (h, p) =>
      {
          if (!h.Skip(h.Count - 2).Contains(p))
          {
              h.Add(p);
          }
          return h;
      });
      

      结果是history 包含:

      AA BB 抄送 AA DD EE 法郎 AA

      【讨论】:

      • 如果potinetialNew 是"AA", "CC", "AA", "DD", "EE", "FF", "AA" 则中断,为此所需的输出是"AA", "BB", "CC", "AA" , "CC", "AA", "DD", "EE", "FF", "AA"。但是您的输出仍然与您的答案相同
      • @Reddy - 为什么这是所需的输出?
      • 大声笑,这就是他的逻辑有多疯狂。所以他说的字符串数组实际上是一个连续的聊天日志,并且他在下一个新列表中可能会出现相同消息的重复。所以他想从新列表中删除可能的重复项。因此,如果您查看我对第二个列表的输入,就会看到所需的输出,您会注意到AA 将是唯一可能重复的输出。因为消息将是有序的。仍然他想要的输出不正确。如果说 20 条消息相同,则 OP 所需的逻辑将省略一些。但这就是他想要的……
      • @Reddy - 抱歉,我仍然不明白要求是什么。这听起来很奇怪。
      • 是的,这确实很奇怪
      【解决方案5】:

      不确定这在性能方面有多好,但我构建了一个逻辑来实现您想要的。任何人都可以进行调整以使其更清洁。

      List<string> history = new List<string>() { "AA", "BB", "CC", "AA" };
      List<string> potentialNew = new List<string>() { "CC", "AA", "DD", "EE", "FF", "AA" };
      
      var result = ProcessChatLog(history,potentialNew);
      //pass these two list to a function to process the chat log
      

      核心逻辑在这里。

       public List<string> ProcessChatLog(List<string> history, List<string> potentialNew)
          {
              var lastChat = history.Last();  
              var lastChatIndex = history.Count - 1;
              var allIndexWithLastChat = potentialNew.Select((c, i) => new { chat = c, Index = i })
                                         .Where(x => x.chat == lastChat)
                                         .Select(x => x.Index).Reverse().ToList();       
      
              List<int> IndexToClear = new List<int>();
              bool overlapFound = false;
      
              foreach (var index in allIndexWithLastChat)
              {
                  if (!overlapFound)
                  {
                      int hitoryChatIndex = lastChatIndex;
                      IndexToClear.Clear();
                      for (int i = index; i > -1; i--)
                      {
                          if (potentialNew[i] == history[hitoryChatIndex])
                          {
                              IndexToClear.Add(i);
                              if (i == 0)
                              {
                                  overlapFound = true;
                                  break;
                              }
                              hitoryChatIndex--;
                          }
                          else
                          {
                              break;
                          }
                      }
                  }
                  else
                  {
                      IndexToClear.Clear();
                      break;
                  }                             
              }
      
              if(IndexToClear.Count >0)
              {
                  potentialNew.RemoveRange(IndexToClear.Min(), IndexToClear.Count);   
              }
      
              return history.Concat(potentialNew).ToList();
          }
      

      这是一些结果

        history = { "AA", "BB", "CC", "AA" }
        potentialNew = { "CC", "AA", "DD", "EE", "FF", "AA"}
      
        Result = { "AA", "BB","CC", "AA", "DD", "EE", "FF", "AA"}
      

        history = { "AA", "BB","AA", "CC", "AA" }
        potentialNew = { "AA","CC", "AA", "DD", "EE", "FF", "AA"}
      
        Result = { "AA", "BB","AA","CC", "AA", "DD", "EE", "FF", "AA"}
      

        history = { "AA", "BB", "CC", "AA" }
        potentialNew = { "CC", "AA", "CC", "AA", "FF", "AA"}
      
        Result = { "AA", "BB","CC", "AA", "CC", "AA", "FF", "AA"}
      

        history = { "AA", "BB", "CC", "AA" }
        potentialNew = {"AA", "CC", "AA", "DD", "EE", "FF", "AA" }
      
        Result = { "AA", "BB","CC", "AA", "CC", "AA", "DD", "EE", "FF", "AA" }
      

      如果这有帮助,请告诉我。

      但我仍然说这不是您想要的理想输出。因为如果聊天包含相同的消息 20 次,并假设您在 2 个列表中分别包含 11 和 9 个项目,该怎么办。现在根据您想要的输出,您将省略所有 9 条消息新列表作为可能的重复和问题。所以我说不是这个修复,解决方案是跟踪聊天日志中传递的消息,并采取措施不在下一个日志中传递这些消息。这样可以保持逻辑和准确性

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-09
        • 1970-01-01
        • 2017-05-25
        • 2021-09-13
        • 1970-01-01
        • 1970-01-01
        • 2015-10-06
        • 1970-01-01
        相关资源
        最近更新 更多