किसी के पास C # में एक सामान्य सूची को डुप्लिकेट करने की एक त्वरित विधि है?
ICollection<MyClass> withoutDuplicates = new HashSet<MyClass>(inputList);
किसी के पास C # में एक सामान्य सूची को डुप्लिकेट करने की एक त्वरित विधि है?
ICollection<MyClass> withoutDuplicates = new HashSet<MyClass>(inputList);
जवाबों:
शायद आपको हैशसेट का उपयोग करने पर विचार करना चाहिए ।
MSDN लिंक से:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
HashSet<int> evenNumbers = new HashSet<int>();
HashSet<int> oddNumbers = new HashSet<int>();
for (int i = 0; i < 5; i++)
{
// Populate numbers with just even numbers.
evenNumbers.Add(i * 2);
// Populate oddNumbers with just odd numbers.
oddNumbers.Add((i * 2) + 1);
}
Console.Write("evenNumbers contains {0} elements: ", evenNumbers.Count);
DisplaySet(evenNumbers);
Console.Write("oddNumbers contains {0} elements: ", oddNumbers.Count);
DisplaySet(oddNumbers);
// Create a new HashSet populated with even numbers.
HashSet<int> numbers = new HashSet<int>(evenNumbers);
Console.WriteLine("numbers UnionWith oddNumbers...");
numbers.UnionWith(oddNumbers);
Console.Write("numbers contains {0} elements: ", numbers.Count);
DisplaySet(numbers);
}
private static void DisplaySet(HashSet<int> set)
{
Console.Write("{");
foreach (int i in set)
{
Console.Write(" {0}", i);
}
Console.WriteLine(" }");
}
}
/* This example produces output similar to the following:
* evenNumbers contains 5 elements: { 0 2 4 6 8 }
* oddNumbers contains 5 elements: { 1 3 5 7 9 }
* numbers UnionWith oddNumbers...
* numbers contains 10 elements: { 0 2 4 6 8 1 3 5 7 9 }
*/
HashSet
कोई अनुक्रमणिका नहीं है , इसलिए इसका उपयोग करना हमेशा संभव नहीं होता है। मुझे एक बार डुप्लिकेट के बिना एक बड़ी सूची बनानी होगी और फिर इसका उपयोग ListView
वर्चुअल मोड में करना होगा। HashSet<>
पहले बनाने के लिए यह सुपर-फास्ट था और फिर इसे एक में परिवर्तित कर दिया List<>
(ताकि ListView
इंडेक्स द्वारा आइटम एक्सेस कर सकें )। List<>.Contains()
बहुत धीमी है।
यदि आप .Net 3+ का उपयोग कर रहे हैं, तो आप Linq का उपयोग कर सकते हैं।
List<T> withDupes = LoadSomeData();
List<T> noDupes = withDupes.Distinct().ToList();
कैसा रहेगा:
var noDupes = list.Distinct().ToList();
.Net 3.5 में?
बस एक ही प्रकार की सूची के साथ एक HashSet आरंभीकृत करें:
var noDupes = new HashSet<T>(withDupes);
या, यदि आप एक सूची वापस चाहते हैं:
var noDupsList = new HashSet<T>(withDupes).ToList();
List<T>
परिणाम के उपयोग की आवश्यकता हैnew HashSet<T>(withDupes).ToList()
इसे क्रमबद्ध करें, फिर दो और दो को एक दूसरे के बगल में जांचें, क्योंकि डुप्लिकेट एक साथ टकराएंगे।
कुछ इस तरह:
list.Sort();
Int32 index = list.Count - 1;
while (index > 0)
{
if (list[index] == list[index - 1])
{
if (index < list.Count - 1)
(list[index], list[list.Count - 1]) = (list[list.Count - 1], list[index]);
list.RemoveAt(list.Count - 1);
index--;
}
else
index--;
}
टिप्पणियाँ:
RemoveAt
एक बहुत महंगा ऑपरेशन हैList
मुझे इस कमांड का उपयोग करना पसंद है:
List<Store> myStoreList = Service.GetStoreListbyProvince(provinceId)
.GroupBy(s => s.City)
.Select(grp => grp.FirstOrDefault())
.OrderBy(s => s.City)
.ToList();
मेरी सूची में ये क्षेत्र हैं: Id, StoreName, City, PostalCode मैं उन शहरों की सूची को एक ड्रॉपडाउन में दिखाना चाहता था जिनमें डुप्लिकेट मान हैं। समाधान: शहर द्वारा समूह फिर सूची के लिए पहले एक को चुनें।
मुझे उम्मीद है यह मदद करेगा :)
इसने मेरे लिए काम किया। बस उपयोग करें
List<Type> liIDs = liIDs.Distinct().ToList<Type>();
अपने इच्छित प्रकार जैसे "int" से "टाइप" को बदलें।
जैसा कि क्रोनोज़ ने कहा था .Net 3.5 में आप उपयोग कर सकते हैं Distinct()
।
.Net 2 में आप इसकी नकल कर सकते हैं:
public IEnumerable<T> DedupCollection<T> (IEnumerable<T> input)
{
var passedValues = new HashSet<T>();
// Relatively simple dupe check alg used as example
foreach(T item in input)
if(passedValues.Add(item)) // True if item is new
yield return item;
}
इसका उपयोग किसी भी संग्रह को पूरा करने के लिए किया जा सकता है और मूल क्रम में मूल्यों को लौटा देगा।
सामान्य रूप से संग्रह को फ़िल्टर करना बहुत तेज़ है (जैसा कि Distinct()
यह और यह नमूना दोनों करता है) इससे आइटम हटाने के लिए होगा।
HashSet
निर्माणकर्ता ने कटौती की, जो इसे अधिकांश परिस्थितियों के लिए बेहतर बनाता है। हालाँकि, यह सॉर्ट क्रम को संरक्षित करेगा, जो HashSet
नहीं करता है।
Dictionary<T, object>
बजाय का उपयोग कर सकते हैं , के .Contains
साथ .ContainsKey
और के .Add(item)
साथ प्रतिस्थापित.Add(item, null)
HashSet
जबकि ऑर्डर सुरक्षित Distinct()
नहीं रखता है ।
एक विस्तार विधि जाने के लिए एक सभ्य तरीका हो सकता है ... कुछ इस तरह से:
public static List<T> Deduplicate<T>(this List<T> listToDeduplicate)
{
return listToDeduplicate.Distinct().ToList();
}
और फिर इस तरह से कॉल करें, उदाहरण के लिए:
List<int> myFilteredList = unfilteredList.Deduplicate();
जावा में (मुझे लगता है कि C # कमोबेश समान है):
list = new ArrayList<T>(new HashSet<T>(list))
यदि आप वास्तव में मूल सूची को बदलना चाहते हैं:
List<T> noDupes = new ArrayList<T>(new HashSet<T>(list));
list.clear();
list.addAll(noDupes);
आदेश को संरक्षित करने के लिए, बस HashSet को LinkedHashSet से बदल दें।
var noDupes = new HashSet<T>(list); list.Clear(); list.AddRange(noDupes);
:)
यह अलग लेता है (तत्वों को दोहराए बिना तत्व) और इसे फिर से एक सूची में परिवर्तित करें:
List<type> myNoneDuplicateValue = listValueWithDuplicate.Distinct().ToList();
Linq की संघ पद्धति का उपयोग करें ।
नोट: इस समाधान को Linq के ज्ञान की आवश्यकता नहीं है, एक तरफ से यह मौजूद है।
कोड
निम्नलिखित को अपनी कक्षा फ़ाइल के शीर्ष पर जोड़कर शुरू करें:
using System.Linq;
अब, आप डुप्लिकेट को किसी ऑब्जेक्ट से हटाने के लिए निम्नलिखित का उपयोग कर सकते हैं obj1
:
obj1 = obj1.Union(obj1).ToList();
नोट: obj1
अपनी वस्तु के नाम का नाम बदलें ।
यह काम किस प्रकार करता है
संघ कमांड दो स्रोत वस्तुओं में से प्रत्येक प्रविष्टि में से एक को सूचीबद्ध करता है। चूंकि obj1 दोनों स्रोत ऑब्जेक्ट हैं, इसलिए यह प्रत्येक प्रविष्टि में से एक में obj1 को कम करता है।
ToList()
एक नई सूची देता है। यह आवश्यक है, क्योंकि लाइनक आदेश Union
मूल सूची को संशोधित करने या एक नई सूची वापस करने के बजाय एक IEnumerable परिणाम के रूप में परिणाम देता है।
सहायक विधि के रूप में (लिनक के बिना):
public static List<T> Distinct<T>(this List<T> list)
{
return (new HashSet<T>(list)).ToList();
}
यदि आप उस आदेश की परवाह नहीं करते हैं HashSet
, तो आप आइटम को केवल एक में बदल सकते हैं , यदि आप उस आदेश को बनाए रखना चाहते हैं तो आप कुछ ऐसा कर सकते हैं:
var unique = new List<T>();
var hs = new HashSet<T>();
foreach (T t in list)
if (hs.Add(t))
unique.Add(t);
या लिनक तरीका:
var hs = new HashSet<T>();
list.All( x => hs.Add(x) );
संपादित करें:HashSet
विधि है O(N)
समय और O(N)
अंतरिक्ष जबकि छँटाई और फिर अद्वितीय बनाने (के रूप में @ ने सुझाव दिया lassevk और अन्य) है O(N*lgN)
समय और O(1)
अंतरिक्ष तो यह मेरे लिए बहुत स्पष्ट नहीं कर रहा है (के रूप में यह पहली नज़र में था) कि छँटाई रास्ता अवर है (मेरे अस्थायी वोट के लिए माफी ...)
यहाँ बगल में डुप्लिकेट को हटाने के लिए एक विस्तार विधि है। कॉल सॉर्ट () पहले और उसी IComparer में पास। यह Lasse V. Karlsen के संस्करण की तुलना में अधिक कुशल होना चाहिए जो RemoveAt को बार-बार कॉल करता है (जिसके परिणामस्वरूप कई ब्लॉक मेमोरी चालें)।
public static void RemoveAdjacentDuplicates<T>(this List<T> List, IComparer<T> Comparer)
{
int NumUnique = 0;
for (int i = 0; i < List.Count; i++)
if ((i == 0) || (Comparer.Compare(List[NumUnique - 1], List[i]) != 0))
List[NumUnique++] = List[i];
List.RemoveRange(NumUnique, List.Count - NumUnique);
}
केवल यह सुनिश्चित करना आसान हो सकता है कि डुप्लिकेट को सूची में नहीं जोड़ा गया है।
if(items.IndexOf(new_item) < 0)
items.add(new_item)
List<T>.Contains
हर बार विधि का उपयोग कर रहा हूं लेकिन 1,000,000 से अधिक प्रविष्टियों के साथ। यह प्रक्रिया मेरे आवेदन को धीमा कर देती है। मैं List<T>.Distinct().ToList<T>()
इसके बजाय पहले का उपयोग कर रहा हूं ।
.Net 2.0 में एक और तरीका
static void Main(string[] args)
{
List<string> alpha = new List<string>();
for(char a = 'a'; a <= 'd'; a++)
{
alpha.Add(a.ToString());
alpha.Add(a.ToString());
}
Console.WriteLine("Data :");
alpha.ForEach(delegate(string t) { Console.WriteLine(t); });
alpha.ForEach(delegate (string v)
{
if (alpha.FindAll(delegate(string t) { return t == v; }).Count > 1)
alpha.Remove(v);
});
Console.WriteLine("Unique Result :");
alpha.ForEach(delegate(string t) { Console.WriteLine(t);});
Console.ReadKey();
}
हल करने के कई तरीके हैं - सूची में डुप्लिकेट समस्या, उनमें से एक नीचे है:
List<Container> containerList = LoadContainer();//Assume it has duplicates
List<Container> filteredList = new List<Container>();
foreach (var container in containerList)
{
Container duplicateContainer = containerList.Find(delegate(Container checkContainer)
{ return (checkContainer.UniqueId == container.UniqueId); });
//Assume 'UniqueId' is the property of the Container class on which u r making a search
if(!containerList.Contains(duplicateContainer) //Add object when not found in the new class object
{
filteredList.Add(container);
}
}
चीयर्स रवि गणेशन
यहाँ एक सरल समाधान है जिसे किसी भी हार्ड-लाइन LINQ या सूची के किसी भी पूर्व छँटाई की आवश्यकता नहीं है।
private static void CheckForDuplicateItems(List<string> items)
{
if (items == null ||
items.Count == 0)
return;
for (int outerIndex = 0; outerIndex < items.Count; outerIndex++)
{
for (int innerIndex = 0; innerIndex < items.Count; innerIndex++)
{
if (innerIndex == outerIndex) continue;
if (items[outerIndex].Equals(items[innerIndex]))
{
// Duplicate Found
}
}
}
}
डेविड जे का जवाब एक अच्छी विधि है, अतिरिक्त वस्तुओं की कोई आवश्यकता नहीं है, छँटाई, आदि। हालांकि इसे बेहतर बनाया जा सकता है:
for (int innerIndex = items.Count - 1; innerIndex > outerIndex ; innerIndex--)
इसलिए बाहरी लूप पूरी सूची के लिए सबसे ऊपर जाता है, लेकिन आंतरिक लूप नीचे तक जाता है "जब तक बाहरी लूप की स्थिति नहीं हो जाती"।
बाहरी लूप सुनिश्चित करता है कि पूरी सूची संसाधित है, आंतरिक लूप वास्तविक डुप्लिकेट पाता है, वे केवल उस भाग में हो सकते हैं जो बाहरी लूप अभी तक संसाधित नहीं हुआ है।
या यदि आप आंतरिक लूप के लिए नीचे नहीं करना चाहते हैं तो आप बाहरी लूप की शुरुआत कर सकते हैं।
सभी उत्तर सूचियों की प्रतिलिपि बनाते हैं, या एक नई सूची बनाते हैं, या धीमी गति से कार्यों का उपयोग करते हैं, या बस दर्दनाक रूप से धीमी गति से होते हैं।
मेरी समझ में, यह सबसे तेज़ और सबसे सस्ती विधि है जो मुझे पता है (साथ ही, वास्तविक समय भौतिकी अनुकूलन पर विशेष अनुभवी प्रोग्रामर द्वारा समर्थित है)।
// Duplicates will be noticed after a sort O(nLogn)
list.Sort();
// Store the current and last items. Current item declaration is not really needed, and probably optimized by the compiler, but in case it's not...
int lastItem = -1;
int currItem = -1;
int size = list.Count;
// Store the index pointing to the last item we want to keep in the list
int last = size - 1;
// Travel the items from last to first O(n)
for (int i = last; i >= 0; --i)
{
currItem = list[i];
// If this item was the same as the previous one, we don't want it
if (currItem == lastItem)
{
// Overwrite last in current place. It is a swap but we don't need the last
list[i] = list[last];
// Reduce the last index, we don't want that one anymore
last--;
}
// A new item, we store it and continue
else
lastItem = currItem;
}
// We now have an unsorted list with the duplicates at the end.
// Remove the last items just once
list.RemoveRange(last + 1, size - last - 1);
// Sort again O(n logn)
list.Sort();
अंतिम लागत है:
nlogn + n + nlogn = n + 2nlogn = O (nlogn) जो बहुत अच्छा है।
RemoveRange के बारे में ध्यान दें: चूंकि हम सूची की गिनती निर्धारित नहीं कर सकते हैं और निकालें funcions का उपयोग करने से बच सकते हैं, मुझे इस ऑपरेशन की गति का ठीक से पता नहीं है लेकिन मुझे लगता है कि यह सबसे तेज़ तरीका है।
यदि आपके पास टो कक्षाएं हैं Product
और Customer
हम उनकी सूची से डुप्लिकेट आइटम निकालना चाहते हैं
public class Product
{
public int Id { get; set; }
public string ProductName { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string CustomerName { get; set; }
}
आपको नीचे दिए गए फॉर्म में एक जेनेरिक क्लास को परिभाषित करना होगा
public class ItemEqualityComparer<T> : IEqualityComparer<T> where T : class
{
private readonly PropertyInfo _propertyInfo;
public ItemEqualityComparer(string keyItem)
{
_propertyInfo = typeof(T).GetProperty(keyItem, BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
}
public bool Equals(T x, T y)
{
var xValue = _propertyInfo?.GetValue(x, null);
var yValue = _propertyInfo?.GetValue(y, null);
return xValue != null && yValue != null && xValue.Equals(yValue);
}
public int GetHashCode(T obj)
{
var propertyValue = _propertyInfo.GetValue(obj, null);
return propertyValue == null ? 0 : propertyValue.GetHashCode();
}
}
फिर, आप अपनी सूची में डुप्लिकेट आइटम हटा सकते हैं।
var products = new List<Product>
{
new Product{ProductName = "product 1" ,Id = 1,},
new Product{ProductName = "product 2" ,Id = 2,},
new Product{ProductName = "product 2" ,Id = 4,},
new Product{ProductName = "product 2" ,Id = 4,},
};
var productList = products.Distinct(new ItemEqualityComparer<Product>(nameof(Product.Id))).ToList();
var customers = new List<Customer>
{
new Customer{CustomerName = "Customer 1" ,Id = 5,},
new Customer{CustomerName = "Customer 2" ,Id = 5,},
new Customer{CustomerName = "Customer 2" ,Id = 5,},
new Customer{CustomerName = "Customer 2" ,Id = 5,},
};
var customerList = customers.Distinct(new ItemEqualityComparer<Customer>(nameof(Customer.Id))).ToList();
इस कोड को निकालने से डुप्लिकेट आइटम Id
आप अन्य संपत्ति से डुप्लिकेट आइटम को दूर करना चाहते हैं, आप बदल सकते हैं nameof(YourClass.DuplicateProperty)
एक ही nameof(Customer.CustomerName)
तब तक डुप्लीकेट आइटम हटा CustomerName
संपत्ति।
public static void RemoveDuplicates<T>(IList<T> list )
{
if (list == null)
{
return;
}
int i = 1;
while(i<list.Count)
{
int j = 0;
bool remove = false;
while (j < i && !remove)
{
if (list[i].Equals(list[j]))
{
remove = true;
}
j++;
}
if (remove)
{
list.RemoveAt(i);
}
else
{
i++;
}
}
}
एक सरल सहज कार्यान्वयन:
public static List<PointF> RemoveDuplicates(List<PointF> listPoints)
{
List<PointF> result = new List<PointF>();
for (int i = 0; i < listPoints.Count; i++)
{
if (!result.Contains(listPoints[i]))
result.Add(listPoints[i]);
}
return result;
}