如何将两个列表连接起来,其中一个列表包含2个元素?

4
据我所知,对于我的问题,LINQ 和 lambda 表达式是解决之道。在我的办公室里,我有一个IM日志列表,是一个二维数组[X长度][2宽度],以便我可以看到谁和谁IM了。但是这些日志由三位数的用户ID组成,而不是实际名称。
arrIMLog[x][1]

因此,日志条目可能看起来像这样:arrIMLog [0] [0] = 353,arrIMLog [0] [1] = 563表示用户353发送了即时消息给用户563。然后,我有一个用户ID列表,想要通过查阅日志找出他们曾与谁进行过即时通讯。

lstSuspects

例如,lstSuspects(1) = 353,lstSuspects(2) = 563等。我想创建一个新的简单列表lstSuspectsContacted,以便我可以找出每个在lstSuspects上的人已经联系了哪些独特的用户ID(次数无关紧要)。我该怎么做?
var lstSuspectsContacted = (from x in arrIMLog
                            join y in lstSuspects 
             on arrIMLog[0] or arrIMLog[1] equals lstSuspects // join criteria
             select new { arrIMLog[0] or arrIMLog[1]}).ToList();

我遇到的困难是,如果在 lstSuspects 和 arrIMLog 的另一个元素[1]或[0]中存在匹配,则希望选择数组中的[0]或[1]元素。 我不知道如何实现这一点。


我认为你关心的不是lambda表达式,而是LINQ。LINQ是你要寻找的唯一标签。 - user1306322
是的,AB被视为与BA相同,IM(即时通讯)的方向对我来说并不重要。现在唯一重要的事情是,根据IM日志,A和B已经联系过了。我正在尝试弄清楚谁与谁交谈,并且发送者/接收者的区别对我来说不重要。 - MrPatterns
1
需要完全使用LINQ吗?没有使用二维数组的更简单的方法。 - Jun Wei Lee
好吧,随你便。通常人们都希望尽可能快地完成任务。 - user1306322
如果SO认为LINQ不是正确的方法,那么也请告诉我。我只是觉得用LINQ可能可以用比循环更少的代码行数完成这个任务。 - MrPatterns
显示剩余3条评论
3个回答

1

解决这个问题的一种方法是跳出固有思维模式,因为我从来不会将其视为LINQ问题。我的做法是将其重构为网络结构。我们只需要遍历一次日志,如果数据集较大,则可能成为一个问题。

void ShowCommunication(int[][] communication, int[] suspects)
  {
      var table = new Dictionary<int, Suspect>();

      // We are going through everyone, though 
      // communication.Where(t => suspects.Contains(t[0]) || suspects.Contains(t[1]))        
      // could speed it up, although that leaves us with an incomplete graph
      foreach (var chat in communication)
      {
          if (!table.ContainsKey(chat[0])) table[chat[0]] = new Suspect(chat[0]);
          if (!table.ContainsKey(chat[1])) table[chat[1]] = new Suspect(chat[1]);

          // Remove the if-statement if you want the communication in order
          if (!table[chat[0]].CoSuspects.Contains(table[chat[1]]))
          {
            table[chat[0]].CoSuspects.Add(table[chat[1]]);
            table[chat[1]].CoSuspects.Add(table[chat[0]]);
          }
      }

      Console.WriteLine("All members");
      foreach (var key in table)
      {
          Console.WriteLine("{0} talked to {1}", key.Key, string.Join(", ", key.Value.CoSuspects.Select(t => t.ID.ToString())));
      }

      Console.WriteLine("\nSuspected members");
      foreach (var key in table.Where(t => suspects.Contains(t.Key)))
      {
          Console.WriteLine("{0} talked to {1}", key.Key, string.Join(", ", key.Value.CoSuspects.Select(t => t.ID.ToString())));
      }
  }

我的辅助方法和类:

  class Suspect
  {
      public Suspect(int id)
      {
          CoSuspects = new HashSet<Suspect>();
          this.ID = id;
      }
      public int ID { get; set; }
      public HashSet<Suspect> CoSuspects { get; set; }
  }

  int[][] GetRandomData()
  {
      var list = new List<int[]>();
      var random = new Random();

    for (int i = 0; i < 100; i++)
      {
          list.Add(new[] { random.Next(10), random.Next(10) });
      }

      return list.ToArray();
  }

  int[] GetSuspects()
  {
      var random = new Random();

      var list = new List<int>();

    for (int i = 0; i < 3; i++)
      {
          list.Add(random.Next(10));
      }
      return list.ToArray();
  }

感谢您的帮助。正如之前所提到的,对我来说选择一个解决方案很困难,但我选择了Jun的方案,因为他第一个回答了我的问题,并且符合我的问题限制。我知道如何在不使用LINQ/lambda表达式的情况下解决这个问题,但看到您的解决方案对我很有帮助(另一种攻击它的方式)。 - MrPatterns

1

这里有一种解决方案,看起来可能更冗长,但更易于扩展和阅读。

您需要首先定义日志和可疑 POCO。

    public class Log
    {
        /// <summary>
        /// Person initiating the contact
        /// </summary>
        public int From { get; set; }

        /// <summary>
        /// Person that was contacted
        /// </summary>
        public int To { get; set; }
    }

    public class SuspectConnection
    {
        public int SuspectId { get; set; }

        public List<int> Contacts { get; set; }
    }

然后您可以使用LINQ轻松查找连接。

    var suspectConnections = new List<SuspectConnection>();

    foreach (var suspect in suspects)
    {
        var connection = new SuspectConnection() { SuspectId = suspect };

        connection.Contacts = logs.Where(x => x.From == suspect || x.To == suspect).Select(x => x.From == suspect ? x.To : x.From).ToList();
        suspectConnections.Add(connection);
    }

我注意到你在From和To之间做了区分。如果From或To中包含SuspectId,我们如何将另一个元素(To或From)添加到名为suspectConnections的列表中?此外,POCO代表什么? - MrPatterns
1
POCO代表Plain Old CLR Object,基本上意味着一个简单的.NET类(例如Log和SuspectConnection类)。您可以使用C#条件运算符http://msdn.microsoft.com/en-us/library/ty67wk28.aspx将其他元素添加到To或From中出现suspectId。 connection.Contacts = logs.Where(x => x.From == suspect || x.To == suspect).Select(x => x.From == suspect ? x.To : x.From).ToList(); - Jun Wei Lee

1

这是使用lambda连接的快速尝试。请注意,我为联系人对中的每个联系人都使用了两个连接。 我认为这也是最有效的解决方案。

int[][] log = new int[][] {new int[]{1,2},new int[]{2,1},new int[]{1,3},new int[]{2,3},new int[]{3,4},new int[]{4,1}};
List<Suspect> Suspects = new List<Suspect>(){new Suspect(){SuspectId =  1, Name = "Bob"},new Suspect(){SuspectId =  2, Name = "Frank"},new Suspect(){SuspectId =  3, Name = "Jimmy"},new Suspect(){SuspectId =  4, Name = "DrEvil"}};


                //order the contact pairs as  2 --> 1 is the same as 1 --> 2 
 var q = log.Select (x => x.OrderBy (o => o))
                // Put contact record into an object which we have an IComparable for
            .Select (s => new Contact(){A = s.ElementAt(0),B= s.ElementAt(1) })
                //Now eliminate the duplicates
            .Distinct(new ContactComparer()) 
                //get the Name for contact A
            .Join(Suspects, contactKey => contactKey.A, suspectKey => suspectKey.SuspectId,(c,s) => new Contact{A = c.A, AName = s.Name, B = c.B})
                //get the Name for contact B
            .Join(Suspects, contactKey => contactKey.B, suspectKey => suspectKey.SuspectId,(c,s) => new Contact{A = c.A, AName = c.AName, B = c.B, BName = s.Name}) 
            .ToList();



//Classes that were used:

public class Contact
{

    public int A { get; set; }
    public String AName { get; set; }
    public int B { get; set; }
    public String BName { get; set; }

}

public class Suspect
{
    public int SuspectId { get; set; }
    public String Name { get; set; }
}



//We will use this in the .Distinct() linq method, to find the (and remove) the duplicates  
public class ContactComparer : IEqualityComparer<Contact>
{        
    public bool Equals(Contact x, Contact y)
    {

        //Check whether the compared objects reference the same data. 
        if (Object.ReferenceEquals(x, y)) return true;

        //Check whether any of the compared objects is null. 
        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
            return false;

        //Check whether the id fields are equal. 
        return x.A == y.A && x.B == y.B;
    }

      public int GetHashCode(Contact contact)
    {
        //Check whether the object is null 
        if (Object.ReferenceEquals(contact, null)) return 0;

        //Get hash code for the Name field if it is not null. 
        long contactA = contact.A == null ? 0 : contact.A;
        long contactB = contact.B == null ? 0 : contact.A;      

        //Calculate the hash code for the product. 
        return (int)((contactA + contactB) % int.MaxValue);
    }       

}

结果:

Result


谢谢你,Darcy。你的回答和我接受的那个一样好,我从你的例子中学到了新的概念。话虽如此,我选择了Jun的答案,因为我觉得它们都同样出色,但他先回答了。这对我来说是一个艰难的选择。再次感谢你的优秀解决方案。 - MrPatterns
没问题。只要注意他的解决方案具有更高的计算复杂度(即在处理大量数据时性能较差)。 - DarcyThomas
我认为李俊伟的解决方案是O:m*n。flindeberg的解决方案是O:n + m(O:n),这与我的解决方案相同。 - DarcyThomas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接