লিনক দিয়ে দুটি অবজেক্টের তালিকা থেকে একটি তালিকা তৈরি করুন


161

আমার নিম্নলিখিত পরিস্থিতি আছে

class Person
{
    string Name;
    int Value;
    int Change;
}

List<Person> list1;
List<Person> list2;

2 টি তালিকাগুলিকে আমি নতুন করে List<Person> একত্রে মিশ্রিত করতে হবে যদি একই ব্যক্তির সাথে একই রেকর্ডটিতে সেই নামটি থাকে, তালিকার ব্যক্তির মান থাকে, পরিবর্তনের তালিকা 2 হয় - তালিকা 1 এর মান। কোনও সদৃশ না হলে পরিবর্তন 0 হয়


2
লিনাকের কি আসলেই দরকার - কিছুটা লিনাক-ইশ এক্সপ্রেশন সহ একটি দুর্দান্ত ফোরচও করতে পারে।
রাশাক

1
এই মন্তব্যটি প্রশ্নের শিরোনামের সংস্করণ হিসাবে যুক্ত করা এবং প্রকৃত প্রশ্নের মিল নেই: এর প্রকৃত উত্তরটি মাইকের এই উত্তর । সর্বাধিক অন্যান্য উত্তরগুলি কার্যকর হলেও মূল পোস্টার দ্বারা উপস্থাপিত সমস্যাটি সমাধান করে না।
জোশুয়া

উত্তর:


254

লিনক এক্সটেনশন পদ্ধতি ইউনিয়ন ব্যবহার করে এটি সহজেই করা যায়। উদাহরণ স্বরূপ:

var mergedList = list1.Union(list2).ToList();

এটি এমন একটি তালিকা ফিরিয়ে দেবে যাতে দুটি তালিকা একত্রিত হয়ে ডাবলস সরানো হয়। আপনি যদি আমার উদাহরণের মতো ইউনিয়ন এক্সটেনশন পদ্ধতিতে কোনও তুলনাকারী নির্দিষ্ট না করেন তবে এটি আপনার ব্যক্তি শ্রেণিতে ডিফল্ট সমান এবং গেটহ্যাশকোড পদ্ধতি ব্যবহার করবে। উদাহরণস্বরূপ আপনি যদি ব্যক্তির নামের সম্পত্তিটির সাথে তুলনা করে তুলনা করতে চান তবে নিজেকে তুলনা সম্পাদনের জন্য আপনাকে অবশ্যই এই পদ্ধতিগুলি ওভাররাইড করতে হবে। এটি সম্পাদন করতে নিম্নলিখিত কোডের নমুনাটি দেখুন Check আপনাকে অবশ্যই আপনার ব্যক্তি শ্রেণিতে এই কোডটি যুক্ত করতে হবে।

/// <summary>
/// Checks if the provided object is equal to the current Person
/// </summary>
/// <param name="obj">Object to compare to the current Person</param>
/// <returns>True if equal, false if not</returns>
public override bool Equals(object obj)
{        
    // Try to cast the object to compare to to be a Person
    var person = obj as Person;

    return Equals(person);
}

/// <summary>
/// Returns an identifier for this instance
/// </summary>
public override int GetHashCode()
{
    return Name.GetHashCode();
}

/// <summary>
/// Checks if the provided Person is equal to the current Person
/// </summary>
/// <param name="personToCompareTo">Person to compare to the current person</param>
/// <returns>True if equal, false if not</returns>
public bool Equals(Person personToCompareTo)
{
    // Check if person is being compared to a non person. In that case always return false.
    if (personToCompareTo == null) return false;

    // If the person to compare to does not have a Name assigned yet, we can't define if it's the same. Return false.
    if (string.IsNullOrEmpty(personToCompareTo.Name) return false;

    // Check if both person objects contain the same Name. In that case they're assumed equal.
    return Name.Equals(personToCompareTo.Name);
}

আপনি যদি সর্বদা দুটি বস্তুর তুলনা করার জন্য নামটি ব্যবহার করতে আপনার ব্যক্তি শ্রেণির ডিফল্ট সমান পদ্ধতিটি সেট করতে না চান তবে আপনি একটি তুলনামূলক শ্রেণিও লিখতে পারেন যা আইকোয়ালিটি কম্পিউটারের ইন্টারফেস ব্যবহার করে। তারপরে আপনি এই তুলনামূলকটিকে লিনক এক্সটেনশন ইউনিয়ন পদ্ধতিতে দ্বিতীয় প্যারামিটার হিসাবে সরবরাহ করতে পারেন। এই জাতীয় তুলনামূলক পদ্ধতি কীভাবে লিখবেন সে সম্পর্কে আরও তথ্য http://msdn.microsoft.com/en-us/library/system.collections.iequalitycomparer.aspx এ পাওয়া যাবে


10
আমি দেখতে পাচ্ছি না কীভাবে এটি মানগুলির একীকরণ সম্পর্কে প্রশ্নটির উত্তর দেয়।
ওয়াগনার দা সিলভা 21

1
এটি প্রতিক্রিয়া জানায় না, ইউনিয়নে দুটি সেটে কেবল উপস্থিত আইটেম থাকবে, দুটি তালিকার একটিতে উপস্থিত কোনও উপাদান নেই
J4N

7
@ J4N আপনি সম্ভবত বিভ্রান্ত Unionকরছেন Intersect?
কোস

11
রেফারেন্সের জন্য: এছাড়াও রয়েছে Concatযে সদৃশগুলি মার্জ করে না
কোস

7
আপনি কি এই উত্তরটি সম্পাদন করতে আপত্তি করবেন তাই এটি আসলে প্রশ্নের উত্তর দেয়? আমি এটি হাস্যকর বলে মনে করি যে প্রশ্নের উত্তর না দেওয়া সত্ত্বেও একটি উত্তর এত বেশি ভোট দেওয়া হয়েছে, কারণ এটি শিরোনাম এবং একটি বেসিক গুগল ক্যোয়ারির উত্তর দেয় ("লিনক মার্জ তালিকাগুলি")।
Rawling

78

আমি লক্ষ্য করেছি যে এই প্রশ্নটির উত্তর হিসাবে 2 বছর পরে চিহ্নিত করা হয়নি - আমি মনে করি নিকটতম উত্তরটি রিচার্ডস, তবে এটি এটিকে অনেক সহজ করা যায়:

list1.Concat(list2)
    .ToLookup(p => p.Name)
    .Select(g => g.Aggregate((p1, p2) => new Person 
    {
        Name = p1.Name,
        Value = p1.Value, 
        Change = p2.Value - p1.Value 
    }));

যদিও এই ক্ষেত্রে ত্রুটি ঘটবে না যেখানে আপনার উভয় সেটে সদৃশ নাম রয়েছে।

আরও কিছু উত্তর মিলিতকরণ ব্যবহার করার পরামর্শ দিয়েছে - এটি অবশ্যই যাওয়ার উপায় নয় কারণ এটি কেবল সংমিশ্রণ না করেই আপনাকে একটি আলাদা তালিকা অর্জন করবে।


8
এই পোস্টটি আসলে প্রশ্নের উত্তর দেয়, এবং এটি ভাল করে।
ফিলু

3
এটি গ্রহণযোগ্য উত্তর হওয়া উচিত। উত্তরের জন্য এতগুলি আপগেট সহ কোনও প্রশ্ন কখনও দেখেনি যা জিজ্ঞাসিত প্রশ্নের উত্তর দেয় না!
টড মেনিয়ার

চমৎকার উত্তর. আমি এটিতে একটি ছোট পরিবর্তন করতে পারি, সুতরাং মানটি হ'ল তালিকা 2 থেকে পাওয়া মান এবং তাই আপনার যদি সদৃশ থাকে তবে পরিবর্তনটি বজায় থাকে: মান = p2. মূল্য এবং পরিবর্তন = p1 সেট করুন + পরিবর্তন + পি 2.ভ্যালু - পি 1.ভ্যালু
রবি দেশাই

70

আপনি শুধু ব্যবহার করবেন না কেন Concat?

কনক্যাট লিঙ্কের একটি অংশ এবং এটি করার চেয়ে বেশি দক্ষ AddRange()

তোমার ক্ষেত্রে:

List<Person> list1 = ...
List<Person> list2 = ...
List<Person> total = list1.Concat(list2);

13
আপনি কীভাবে জানেন যে এটি আরও দক্ষ?
জেরি নিকসন

@ জেরি নিকসন তিনি / তিনি এটি পরীক্ষা করেন নি, তবে ব্যাখ্যাটি যৌক্তিক বলে মনে হচ্ছে। stackoverflow.com/questions/1337699/…
নুলিয়াস

9
stackoverflow.com/questions/100196/net-listt-concat-vs-addrange -> গ্রেগের মন্তব্য: Actually, due to deferred execution, using Concat would likely be faster because it avoids object allocation - Concat doesn't copy anything, it just creates links between the lists so when enumerating and you reach the end of one it transparently takes you to the start of the next! এটি আমার বক্তব্য।
J4N

2
এবং সুবিধাটি হ'ল যদি আপনি সত্তা ফ্রেমওয়ার্ক ব্যবহার করেন তবে এটি সি # পাশের পরিবর্তে এসকিউএল দিকে করা যেতে পারে।
J4N

4
আসল কারণ এটি সাহায্য করে না তা হ'ল এটি উভয় তালিকায় উপস্থিত কোনও বস্তুকেই মার্জ করে না।
মাইক Goatly

15

এই লিনক

var mergedList = list1.Union(list2).ToList();

এটি নরমালি (অ্যাডরেঞ্জ)

var mergedList=new List<Person>();
mergeList.AddRange(list1);
mergeList.AddRange(list2);

এটি নরমালি (ফরচ)

var mergedList=new List<Person>();

foreach(var item in list1)
{
    mergedList.Add(item);
}
foreach(var item in list2)
{
     mergedList.Add(item);
}

এটি নরমালি (ফরচ-ডাবলিস)

var mergedList=new List<Person>();

foreach(var item in list1)
{
    mergedList.Add(item);
}
foreach(var item in list2)
{
   if(!mergedList.Contains(item))
   {
     mergedList.Add(item);
   }
}

12

এটি করার জন্য কয়েকটি টুকরো রয়েছে, ধরে নিলাম প্রতিটি তালিকায় সদৃশ থাকে না, নাম একটি অনন্য শনাক্তকারী, এবং উভয়ই তালিকা অর্ডার করা হয় না।

একটি একক তালিকা পেতে প্রথমে একটি অ্যাপেনড এক্সটেনশন পদ্ধতি তৈরি করুন:

static class Ext {
  public static IEnumerable<T> Append(this IEnumerable<T> source,
                                      IEnumerable<T> second) {
    foreach (T t in source) { yield return t; }
    foreach (T t in second) { yield return t; }
  }
}

সুতরাং একটি একক তালিকা পেতে পারেন:

var oneList = list1.Append(list2);

তারপরে নামে গ্রুপ করুন

var grouped = oneList.Group(p => p.Name);

তারপরে প্রতিটি গ্রুপকে একযোগে একটি গোষ্ঠী প্রক্রিয়া করার জন্য সহায়তার সাহায্যে প্রক্রিয়া করতে পারেন

public Person MergePersonGroup(IGrouping<string, Person> pGroup) {
  var l = pGroup.ToList(); // Avoid multiple enumeration.
  var first = l.First();
  var result = new Person {
    Name = first.Name,
    Value = first.Value
  };
  if (l.Count() == 1) {
    return result;
  } else if (l.Count() == 2) {
    result.Change = first.Value - l.Last().Value;
    return result;
  } else {
    throw new ApplicationException("Too many " + result.Name);
  }
}

যা প্রতিটি উপাদানের জন্য প্রয়োগ করা যেতে পারে grouped:

var finalResult = grouped.Select(g => MergePersonGroup(g));

(সতর্কতা: অরক্ষিত)


2
তোমার Appendএকটি প্রায় সঠিক আউট-অফ-বাক্সের সদৃশ Concat
রোলিং

@ রোলিং: এটি হ'ল কোনও কারণে আমি নিখোঁজ রয়েছি Enumerable.Concatএবং এভাবে এটি পুনরায় বাস্তবায়ন করেছি।
রিচার্ড

2

আপনার সম্পূর্ণ বাইরের যোগদানের মতো কিছু দরকার। System.Linq.Enumerable এর এমন কোনও পদ্ধতি নেই যা সম্পূর্ণ বাহ্যিক জোড় প্রয়োগ করে, তাই আমাদের এটি নিজেরাই করতে হবে।

var dict1 = list1.ToDictionary(l1 => l1.Name);
var dict2 = list2.ToDictionary(l2 => l2.Name);
    //get the full list of names.
var names = dict1.Keys.Union(dict2.Keys).ToList();
    //produce results
var result = names
.Select( name =>
{
  Person p1 = dict1.ContainsKey(name) ? dict1[name] : null;
  Person p2 = dict2.ContainsKey(name) ? dict2[name] : null;
      //left only
  if (p2 == null)
  {
    p1.Change = 0;
    return p1;
  }
      //right only
  if (p1 == null)
  {
    p2.Change = 0;
    return p2;
  }
      //both
  p2.Change = p2.Value - p1.Value;
  return p2;
}).ToList();

2

নীচের কোডটি কি আপনার সমস্যার জন্য কাজ করে? তালিকার সংমিশ্রণ করতে আমি ভিতরে কিছুটা লিনকের সাথে একটি ফোরচ ব্যবহার করেছি এবং ধরে নিয়েছি যে লোকেরা তাদের নামের সাথে মেলে যদি সমান হয়, এবং মনে হয় এটি রান করার সময় প্রত্যাশিত মানগুলি মুদ্রণ করে। পূর্বেভাগকে লিনকে রূপান্তর করার জন্য রিশার্পার কোনও পরামর্শ দেয় না তাই এটি সম্ভবত এটি এতটা ভাল যে এটি এটি এভাবে করা হবে।

public class Person
{
   public string Name { get; set; }
   public int Value { get; set; }
   public int Change { get; set; }

   public Person(string name, int value)
   {
      Name = name;
      Value = value;
      Change = 0;
   }
}


class Program
{
   static void Main(string[] args)
   {
      List<Person> list1 = new List<Person>
                              {
                                 new Person("a", 1),
                                 new Person("b", 2),
                                 new Person("c", 3),
                                 new Person("d", 4)
                              };
      List<Person> list2 = new List<Person>
                              {
                                 new Person("a", 4),
                                 new Person("b", 5),
                                 new Person("e", 6),
                                 new Person("f", 7)
                              };

      List<Person> list3 = list2.ToList();

      foreach (var person in list1)
      {
         var existingPerson = list3.FirstOrDefault(x => x.Name == person.Name);
         if (existingPerson != null)
         {
            existingPerson.Change = existingPerson.Value - person.Value;
         }
         else
         {
            list3.Add(person);
         }
      }

      foreach (var person in list3)
      {
         Console.WriteLine("{0} {1} {2} ", person.Name,person.Value,person.Change);
      }
      Console.Read();
   }
}

1
public void Linq95()
{
    List<Customer> customers = GetCustomerList();
    List<Product> products = GetProductList();

    var customerNames =
        from c in customers
        select c.CompanyName;
    var productNames =
        from p in products
        select p.ProductName;

    var allNames = customerNames.Concat(productNames);

    Console.WriteLine("Customer and product names:");
    foreach (var n in allNames)
    {
        Console.WriteLine(n);
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.