Given a list of lists I am looking to create all possible combinations.
Example:
I have a list which holds 3 lists
List 1: Apple, Banana, Pear
List 2: Bed, Chair
List 3: Ben, Bob, Carl, Phil
From this I would expect to end up with a List of combinations
Apple_Bed_Ben
Apple_Bed_Bob
Apple_Bed_Carl
Apple_Bed_Phil
Apple_Chair_Ben
Apple_Chair_Bob
Apple_Chair_Carl
Apple_Chair_Phil
Banana_Bed_Ben
Banana_Bed_Bob
...
I don't know if I am missing something but I have been going in circles for hours now.
If I knew there would only ever be three lists I know I could just use nested for loops going through building the combination string but here there could be any number of lists.
Can anyone point me in the right direction to get this done?
This is what i currently have:
public class ChildrenNames
{
public string parentName;
public int numberOfNames;
public List<string> childrenNames = new List<string>();
}
public class Combination
{
public bool selected = true;
public string name;
}
List<Combination> GetAllCombinations()
{
List<Combination> allCombinations = new List<Combination>();
List<ChildrenNames> listOfChildren = new List<ChildrenNames>();
//Create list of children names for each parent object
for (int p = 0; p < parentObjects.Count; p++)
{
ChildrenNames cn = new ChildrenNames();
for (int c = 0; c < parentObjects[p].transform.childCount; c++)
cn.childrenNames.Add(parentObjects[p].transform.GetChild(c).name);
cn.parentName = parentObjects[p].name;
cn.numberOfNames = cn.childrenNames.Count;
listOfChildren.Add(cn);
}
for (int l = 0; l < listOfChildren.Count; l++)
{
for (int c = 0; c < listOfChildren[l].numberOfNames; c++)
{
if (l == 0)
{
for (int p = 0; p < listOfChildren.Count; p ++)
{
Combination combination = new Combination();
combination.name = listOfChildren[l].childrenNames[c];
allCombinations.Add(combination);
}
}
else
{
for (int i = 0; i < allCombinations.Count; i++)
allCombinations[i].name += "_" + listOfChildren[l].childrenNames[c];
}
}
}
return allCombinations;
}
This creates the correct number of combinations but for example throws out
Apple_Bed_Chair_Ben_Bob_Carl_Phil
I understand why this is happening but not how I can change this to get the expected result.
You need to keep track of the column for each of the lists in order for it to work properly.
public List<string> ZipStringLists(params List<string>[] lists)
{
var columnNo = new int[lists.Length];
var resultingList = new List<string>();
var stringBuilder = new StringBuilder();
while (columnNo[0] < lists[0].Count)
{
// Combine the items into one: Apple + Banana + Pear = AppleBananaPear
for (int i = 0; i < lists.Length; i++)
{
var listElement = lists[i];
// columnNo[i] contains which column to write out for the individual list
stringBuilder.Append(listElement[columnNo[i]]);
}
// Write out the result and add it to a result list for later retrieval
var result = stringBuilder.ToString();
resultingList.Add(result);
Console.WriteLine(result);
stringBuilder.Clear();
// We increment columnNo from the right to the left
// The next item after AppleBedBen is AppleBedBob
// Overflow to the next column happens when a column reaches its maximum value
for (int i = lists.Length - 1; i >= 0; i--)
{
if (++columnNo[i] == lists[i].Count
&& i != 0 /* The last column overflows when the computation finishes */)
{
// Begin with 0 again on overflow and continue to add to the next column
columnNo[i] = 0;
}
else
{
// No overflow -> stop
break;
}
}
}
return resultingList;
}
Usage:
List<string> list1 = new List<string> { "Apple", "Banana", "Pear" };
List<string> list2 = new List<string> { "Bed", "Chair" };
List<string> list3 = new List<string> { "Ben", "Bob", "Carl", "Phil" };
ZipStringLists(list1, list2, list3);
You could use a fairly generic solution that accepts any number of lists to incrementally build up the combinations. It's short, though not necessarily as optimal as other solutions as it builds intermediate lists:
public List<string> FindCombinations(params List<string>[] lists)
{
List<string> combinations = lists[0];
for (int i = 1; i < lists.Length; i++)
{
List<string> newCombinations = new List<string>(combinations.Count * lists[i].Count);
combinations.ForEach(s1 => lists[i].ForEach(s2 => newCombinations.Add($"{s1}_{s2}")));
combinations = newCombinations;
}
return combinations;
}
Usage:
List<string> combinations = FindCombinations(list1, list2, list3, list4, list5...)
I'm making a basic Deal or No Deal game, in doing so I have to pick 10 finalists from an array, at random, without repeats.
I have my structure and arrays set out like this
public struct People
{
public string firstname;
public string lastname;
public int age;
}
class Program
{
public static People[] People1 = new People[40];
public static People[] Finalists1 = new People[10];
public static People[] Finalist1 = new People[1];
And my finalists method set out like this
Random rand = new Random();
for (int i = 0; i < Finalists1.Length; i++)
{
num = rand.Next(0, People1.Length);
Finalists1[i].lastname = People1[num].lastname;
Finalists1[i].firstname = People1[num].firstname;
Finalists1[i].age = People1[num].age;
}
How can I eliminate duplicate entries, while maintaining 10 people in the array?
Since initial array doesn't contain duplicates, you can sort it in random order and pick up 10 top items:
Finalists1 = People1
.OrderByDescending(item => 1) // if people have some points, bonuses etc.
.ThenBy(item => Guid.NewGuid()) // shuffle among peers
.Take(10) // Take top 10
.ToArray(); // materialize as an array
If people are selected to the final are not completely random (e.g. contestant can earn points, bonuses etc.) change .OrderByDescending(item => 1), e.g.
.OrderByDescending(item => item.Bonuses)
If you don't want to use Linq, you can just draw Peoples from urn without returning:
private static Random random = new Random();
...
List<People> urn = new List<People>(People1);
for (int i = 0; i < Finalists1.Length; ++i) {
int index = random.Next(0, urn.Count);
Finalists1[i] = urn[index];
urn.RemoveAt(index);
}
You can hold a list or hash set of numbers you have already drawn. Then just roll the dice again to get another random number.
Random rand = new Random();
HashSet<int> drawnNumbers = new HashSet<int>();
for (int i = 0; i < Finalists1.Length; i++)
{
do
{
num = rand.Next(0, People1.Length);
}
while (drawnNumbers.Contains(num));
Finalists1[i] = People1[num];
}
You can change the type of Finalists1 to a HashSet, that does not allow duplicates.
Then change your loop to
while(Finalists1.Length < 10)
{
// random pick from array People1 (you don't need to create a new one)
num = rand.Next(0, People1.Length);
var toAdd = People1[num];
// add to hash-set. Object won't be added, if already existing in the set
Finalists1.Add(toAdd);
}
You probably need to override the Equals method of class People, if you really need to create a new object to add to the hash-set.
You can group people array and select distinct that way.
If you use List you can remove person from the list
`var peopleArray = new People[40];
var peopleCollection = peopleArray.GroupBy(p => new { p.age, p.firstname, p.lastname }).Select(grp => grp.FirstOrDefault()).ToList();
var finalists = new People[10];
var rand = new Random();
for (var i = 0; i < finalists.Length; i++)
{
var index = rand.Next(0, peopleCollection.Count);
var person = peopleCollection[index];
finalists[i].lastname = person.lastname;
finalists[i].firstname = person.firstname;
finalists[i].age = person.age;
peopleCollection.Remove(person);
}
shuffle and take the first 10, for example
People1.Shuffle();
Finalists1= People1.Take(10).ToArray();
you can find shuffle code from StackOverflow or search for "Fisher-Yates shuffle C#" Below methods are taken from This SO Post. Read the answers for more information on why GUID is not used etc..
public static class ThreadSafeRandom
{
[ThreadStatic] private static Random Local;
public static Random ThisThreadsRandom
{
get { return Local ?? (Local = new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); }
}
}
static class MyExtensions
{
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
Swap each selected element in People1 to with the end of the array, and decrement an end-of-array index so that you're only selecting from what's left on the next iteration.
People tempPerson = new People;
int lastElem = People1.length - 1;
for (int i = 0; i < Finalists1.Length; i++)
{
num = rand.Next(0, lastElem + 1);
Finalists1[i] = People1[num];
//swap last entry in People1 with People1[num]
tempPerson = People1[num];
People1[num] = People1[lastElem];
People1[lastElem] = tempPerson;
lastElem--;
}
Sorry if there's a syntax error, I'm mostly using Java and C# these days.
BTW You don't have to set the fields individually since each array stores objects of type Person.
I have a method which takes in N, the number of objects I want to create, and I need to return a list of N objects.
Currently I can do this with a simple loop:
private static IEnumerable<MyObj> Create(int count, string foo)
{
var myList = new List<MyObj>();
for (var i = 0; i < count; i++)
{
myList .Add(new MyObj
{
bar = foo
});
}
return myList;
}
And I'm wondering if there is another way, maybe with LINQ to create this list.
I've tried:
private static IEnumerable<MyObj> CreatePaxPriceTypes(int count, string foo)
{
var myList = new List<MyObj>(count);
return myList.Select(x => x = new MyObj
{
bar = foo
});
}
But this does seem to populate my list.
I tried changing the select to a foreach but its the same deal.
I realized that the list has the capacity of count and LINQ is not finding any elements to iterate.
myList.ForEach(x => x = new MyObj
{
bar = foo
});
Is there a correct LINQ operator to use to get this to work? Or should I just stick with the loop?
You can use the Range to create a sequence:
return Enumerable.Range(0, count).Select(x => new MyObj { bar = foo });
If you want to create a List, you'd have to ToList it.
Mind you though, it's (arguably) a non-obvious solution, so don't throw out the iterative way of creating the list just yet.
You could create generic helper methods, like so:
// Func<int, T>: The int parameter will be the index of each element being created.
public static IEnumerable<T> CreateSequence<T>(Func<int, T> elementCreator, int count)
{
if (elementCreator == null)
throw new ArgumentNullException("elementCreator");
for (int i = 0; i < count; ++i)
yield return (elementCreator(i));
}
public static IEnumerable<T> CreateSequence<T>(Func<T> elementCreator, int count)
{
if (elementCreator == null)
throw new ArgumentNullException("elementCreator");
for (int i = 0; i < count; ++i)
yield return (elementCreator());
}
Then you could use them like this:
int count = 100;
var strList = CreateSequence(index => index.ToString(), count).ToList();
string foo = "foo";
var myList = CreateSequence(() => new MyObj{ Bar = foo }, count).ToList();
You can Use Enumerable.Repeat
IEnumerable<MyObject> listOfMyObjetcs = Enumerable.Repeat(CreateMyObject, numberOfObjects);
For more info read https://msdn.microsoft.com/en-us/library/bb348899(v=vs.110).aspx
Lets say I have List<string> = new List<string>() {"20","26","32"}
I want to create a new List based on the first number in the previous list and it should have the same number of elements in it. I will be adding a certain number to that first number and so on and so on. As an example, using 6 as the number to add I would get 20,26,32. The resulting list will be List. The number 6 is a class wide property.
The issue comes if I have a list of "N","N","32"
I need to produce the same list of 20,26,32 but I have to use the last number to work out the others.
If I had "N","26","N" I would have to use the middle number to work out the others.
The N represents no data in the input list and it will always be this character
In summary, I need to produce a new list with the same number of elements as the input list and it must take the first or next numerical element to produce the resulting list using a specified number to add/subtract values to.
I wondered if LINQ's aggregate function might be able to handle it but got a bit lost using it.
Examples:
"20","26","32" = 20,26,32
"N","26","32" = 20,26,32
"N","N","32" = 20,26,32
"20","26","N" = 20,26,32
What about something like this:
var n = 6;
List<string> strList = new List<string>() {"20","26","32"};
// list can also be {null, "26", null} , {null, "N", "32"} ,
// {"N", "26", null } etc...
var list = strList.Select(s =>
{
int v;
if(string.IsNullOrEmpty(s) || !int.TryParse(s,out v))
return (int?)null;
return v;
});
var firstValidVal = list.Select((Num, Index) => new { Num, Index })
.FirstOrDefault(x => x.Num.HasValue);
if(firstValidVal == null)
throw new Exception("No valid number found");
var bases = Enumerable.Range(0, strList.Count).Select(i => i * n);
int startVal = firstValidVal.Num.Value - bases.ElementAt(firstValidVal.Index);
var completeSequence = bases.Select(x => x + startVal);
It sounds like you want a function which will
Take a List<int> as input
Make the first element of the original list the first element of the new list
New list has same number of elements as original
Remaining numbers are the first element + a value * position
If so then try the following
static bool TryGetFirstNumber(List<string> list, out number, out index) {
for (var i = 0; i < list.Count; i++) {
var cur = list[0];
if (!String.IsNullOrEmpty(cur) && Int32.TryParse(cur, out number)) {
index = i;
return true;
}
}
number = 0;
index = 0;
return false;
}
static List<T> TheFunction(List<string> list, int increment) {
var newList = new List<int>();
int first;
int index;
if (TryGetFirstNumber(list, out first, out index)) {
first -= index * increment;
} else {
first = 0;
}
newList.Add(first);
for (var i = 1; i < list.Length; i++) {
newList.Add(first + increment);
increment += increment;
}
return newList;
}
For LINQ purposes, I sometimes resort to writing a parse method that returns an int?as the result so that I can return null when it fails to parse. Here's a complete LINQPad implementation that illustrates this and the positional select (taking an approach otherwise similar to digEmAll's):
void Main()
{
var n = 6;
var items = new List<string>
// {"20","N", "N"};
// {"N", "26", "N"};
{"N", "N", "32"};
var first = items
.Select((v,index) => new { val = Parse(v), index })
.First(x => x.val.HasValue);
int start = first.val.Value - n * first.index;
List<string> values = items
.Select((x,i) => (i * n + start).ToString())
.ToList();
}
int? Parse(string strVal)
{
int ret;
if (int.TryParse(strVal, out ret))
{
return ret;
}
return null;
}
Seems like a lot of work to do something kinda simple. Here is a non linq approach.
private List<int> getVals(List<string> input, int modifier)
{
if (input == null) return null; if (input.Count < 1) return null;
foreach (var s in input)
{
int i;
try{i = Convert.ToInt32(s);}
catch{continue;}
var returnList = new List<int>(input.Count);
for (int n = 0; n < input.Count;n++ )returnList[n] = ((n - input.IndexOf(s)) * modifier) + i;
return returnList;
}
return null;
}
DevGeezer's answer, but without the cruft.
But I still learned alot!
static List<String> genlist2(List<String> list, int interval)
{
if (list == null) return null;
var vali = list
.Select((x, i) => x != "N" ? new {val = Convert.ToInt32(x), i } : null)
.First(x => x != null);
if (vali == null) return list.ToList();
return Enumerable.Range(0, list.Count)
.Select(x => (vali.val - (vali.i - x) * interval).ToString())
.ToList();
}
Anyone have a quick method for de-duplicating a generic List in C#?
If you're using .Net 3+, you can use Linq.
List<T> withDupes = LoadSomeData();
List<T> noDupes = withDupes.Distinct().ToList();
Perhaps you should consider using a HashSet.
From the MSDN link:
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 }
*/
How about:
var noDupes = list.Distinct().ToList();
In .net 3.5?
Simply initialize a HashSet with a List of the same type:
var noDupes = new HashSet<T>(withDupes);
Or, if you want a List returned:
var noDupsList = new HashSet<T>(withDupes).ToList();
Sort it, then check two and two next to each others, as the duplicates will clump together.
Something like this:
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--;
}
Notes:
Comparison is done from back to front, to avoid having to resort list after each removal
This example now uses C# Value Tuples to do the swapping, substitute with appropriate code if you can't use that
The end-result is no longer sorted
I like to use this command:
List<Store> myStoreList = Service.GetStoreListbyProvince(provinceId)
.GroupBy(s => s.City)
.Select(grp => grp.FirstOrDefault())
.OrderBy(s => s.City)
.ToList();
I have these fields in my list: Id, StoreName, City, PostalCode
I wanted to show list of cities in a dropdown which has duplicate values.
solution: Group by city then pick the first one for the list.
It worked for me. simply use
List<Type> liIDs = liIDs.Distinct().ToList<Type>();
Replace "Type" with your desired type e.g. int.
As kronoz said in .Net 3.5 you can use Distinct().
In .Net 2 you could mimic it:
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;
}
This could be used to dedupe any collection and will return the values in the original order.
It's normally much quicker to filter a collection (as both Distinct() and this sample does) than it would be to remove items from it.
An extension method might be a decent way to go... something like this:
public static List<T> Deduplicate<T>(this List<T> listToDeduplicate)
{
return listToDeduplicate.Distinct().ToList();
}
And then call like this, for example:
List<int> myFilteredList = unfilteredList.Deduplicate();
In Java (I assume C# is more or less identical):
list = new ArrayList<T>(new HashSet<T>(list))
If you really wanted to mutate the original list:
List<T> noDupes = new ArrayList<T>(new HashSet<T>(list));
list.clear();
list.addAll(noDupes);
To preserve order, simply replace HashSet with LinkedHashSet.
This takes distinct (the elements without duplicating elements) and convert it into a list again:
List<type> myNoneDuplicateValue = listValueWithDuplicate.Distinct().ToList();
Use Linq's Union method.
Note: This solution requires no knowledge of Linq, aside from that it exists.
Code
Begin by adding the following to the top of your class file:
using System.Linq;
Now, you can use the following to remove duplicates from an object called, obj1:
obj1 = obj1.Union(obj1).ToList();
Note: Rename obj1 to the name of your object.
How it works
The Union command lists one of each entry of two source objects. Since obj1 is both source objects, this reduces obj1 to one of each entry.
The ToList() returns a new List. This is necessary, because Linq commands like Union returns the result as an IEnumerable result instead of modifying the original List or returning a new List.
As a helper method (without Linq):
public static List<T> Distinct<T>(this List<T> list)
{
return (new HashSet<T>(list)).ToList();
}
Here's an extension method for removing adjacent duplicates in-situ. Call Sort() first and pass in the same IComparer. This should be more efficient than Lasse V. Karlsen's version which calls RemoveAt repeatedly (resulting in multiple block memory moves).
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);
}
Installing the MoreLINQ package via Nuget, you can easily distinct object list by a property
IEnumerable<Catalogue> distinctCatalogues = catalogues.DistinctBy(c => c.CatalogueCode);
If you have tow classes Product and Customer and we want to remove duplicate items from their list
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; }
}
You must define a generic class in the form below
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();
}
}
then, You can remove duplicate items in your list.
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();
this code remove duplicate items by Id if you want remove duplicate items by other property, you can change nameof(YourClass.DuplicateProperty) same nameof(Customer.CustomerName) then remove duplicate items by CustomerName Property.
If you don't care about the order you can just shove the items into a HashSet, if you do want to maintain the order you can do something like this:
var unique = new List<T>();
var hs = new HashSet<T>();
foreach (T t in list)
if (hs.Add(t))
unique.Add(t);
Or the Linq way:
var hs = new HashSet<T>();
list.All( x => hs.Add(x) );
Edit: The HashSet method is O(N) time and O(N) space while sorting and then making unique (as suggested by #lassevk and others) is O(N*lgN) time and O(1) space so it's not so clear to me (as it was at first glance) that the sorting way is inferior
Might be easier to simply make sure that duplicates are not added to the list.
if(items.IndexOf(new_item) < 0)
items.add(new_item)
You can use Union
obj2 = obj1.Union(obj1).ToList();
Another way in .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();
}
There are many ways to solve - the duplicates issue in the List, below is one of them:
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);
}
}
Cheers
Ravi Ganesan
Here's a simple solution that doesn't require any hard-to-read LINQ or any prior sorting of the list.
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
}
}
}
}
David J.'s answer is a good method, no need for extra objects, sorting, etc. It can be improved on however:
for (int innerIndex = items.Count - 1; innerIndex > outerIndex ; innerIndex--)
So the outer loop goes top bottom for the entire list, but the inner loop goes bottom "until the outer loop position is reached".
The outer loop makes sure the entire list is processed, the inner loop finds the actual duplicates, those can only happen in the part that the outer loop hasn't processed yet.
Or if you don't want to do bottom up for the inner loop you could have the inner loop start at outerIndex + 1.
A simple intuitive implementation:
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;
}
All answers copy lists, or create a new list, or use slow functions, or are just painfully slow.
To my understanding, this is the fastest and cheapest method I know (also, backed by a very experienced programmer specialized on real-time physics optimization).
// 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();
Final cost is:
nlogn + n + nlogn = n + 2nlogn = O(nlogn) which is pretty nice.
Note about RemoveRange:
Since we cannot set the count of the list and avoid using the Remove funcions, I don't know exactly the speed of this operation but I guess it is the fastest way.
Using HashSet this can be done easily.
List<int> listWithDuplicates = new List<int> { 1, 2, 1, 2, 3, 4, 5 };
HashSet<int> hashWithoutDuplicates = new HashSet<int> ( listWithDuplicates );
List<int> listWithoutDuplicates = hashWithoutDuplicates.ToList();
Using HashSet:
list = new HashSet<T>(list).ToList();
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++;
}
}
}
If you need to compare complex objects, you will need to pass a Comparer object inside the Distinct() method.
private void GetDistinctItemList(List<MyListItem> _listWithDuplicates)
{
//It might be a good idea to create MyListItemComparer
//elsewhere and cache it for performance.
List<MyListItem> _listWithoutDuplicates = _listWithDuplicates.Distinct(new MyListItemComparer()).ToList();
//Choose the line below instead, if you have a situation where there is a chance to change the list while Distinct() is running.
//ToArray() is used to solve "Collection was modified; enumeration operation may not execute" error.
//List<MyListItem> _listWithoutDuplicates = _listWithDuplicates.ToArray().Distinct(new MyListItemComparer()).ToList();
return _listWithoutDuplicates;
}
Assuming you have 2 other classes like:
public class MyListItemComparer : IEqualityComparer<MyListItem>
{
public bool Equals(MyListItem x, MyListItem y)
{
return x != null
&& y != null
&& x.A == y.A
&& x.B.Equals(y.B);
&& x.C.ToString().Equals(y.C.ToString());
}
public int GetHashCode(MyListItem codeh)
{
return codeh.GetHashCode();
}
}
And:
public class MyListItem
{
public int A { get; }
public string B { get; }
public MyEnum C { get; }
public MyListItem(int a, string b, MyEnum c)
{
A = a;
B = b;
C = c;
}
}
I think the simplest way is:
Create a new list and add unique item.
Example:
class MyList{
int id;
string date;
string email;
}
List<MyList> ml = new Mylist();
ml.Add(new MyList(){
id = 1;
date = "2020/09/06";
email = "zarezadeh#gmailcom"
});
ml.Add(new MyList(){
id = 2;
date = "2020/09/01";
email = "zarezadeh#gmailcom"
});
List<MyList> New_ml = new Mylist();
foreach (var item in ml)
{
if (New_ml.Where(w => w.email == item.email).SingleOrDefault() == null)
{
New_ml.Add(new MyList()
{
id = item.id,
date = item.date,
email = item.email
});
}
}