how to display unique record in combobox in C# - c#

Can any body help on this.
here i am using datasource as list for combobox.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
LoadDropdownlist();
}
public void LoadDropdownlist()
{
var makes = new List<string> {
"Audi",
"BMW",
"Ford",
"Vauxhall",
"Volkswagen"
};
comboBox1.DataSource = makes;
}
as per above code i am getting result in combobox as
Audi,
BMW,
Ford,
Vauxhall,
Volkswagen
but i want to display unique records based on first character.So i am expecting below result in combobox.
Audi,
BMW,
Ford,
Vauxhall
thanks,
satish

public void LoadDropdownlist()
{
var makes = new List<string> {"Volkswagen","Audi","BMW","Ford","Vauxhall"};
FirstLetterComparer comp = new FirstLetterComparer();
comboBox1.DataSource= makes.Distinct(comp).ToList();//makes.GroupBy(x=>x[0]).Select(x=>x.First()).ToList();
}
public class FirstLetterComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
if (string.IsNullOrEmpty(x) || string.IsNullOrEmpty(y))
return false;
//ignoring case
return string.Compare(x[0].ToString(), y[0].ToString(), 0) == 0;
}
public int GetHashCode(string str)
{
return 1;
}
}
When checking two objects for equality the following happens:
First, GetHashCode is called on both objects. If the hash code is different, the objects are considered not equal and Equals is never called.
Equals is only called when GetHashCode returned the same value for both objects.
Solution for .Net 2
In .Net 2 you need to loop through items as this
public List<string> FetchFirstLetterUniques(List<string> source)
{
List<string> result = new List<string>();
foreach (string item in source)
{
bool isAdded = false;
foreach (string item2 in result)
{
if (string.Compare(item2[0].ToString(), item[0].ToString(), 0) == 0)
{
isAdded = true;
break;
}
}
if (!isAdded)
result.Add(item);
}
return result;
}
public void LoadDropdownlist()
{
var makes = new List<string> {
"Audi",
"BMW",
"Ford",
"Vauxhall",
"Volkswagen"
};
comboBox1.DataSource = FetchFirstLetterUniques(makes);
}

You can group them based on first characters:
comboBox1.DataSource = makes
.GroupBy(x => x[0])
.Select(x => x.First())
.ToList();

try this
var makes = new List<string> {
"Audi",
"BMW",
"Ford",
"Vauxhall",
"Volkswagen"
}.GroupBy(t => t[0])
.Select(t => t.First()).ToList();

Related

c# pass class variable as paramater in method LINQ like?

I have a class with a lot of different string values
I have a collection class holding these with some sorting functions. Currently i have lots of reproduced code for "GetListOfDifferentProducts" "GetListOfDifferentManufacturers" etc.
Is this possible? Or am i going about this the wrong way.
These are all simple foreach loops
public List<string> GetListOfDifferentProducts()
{
List<string> listOfResults = new List<string>();
foreach (Product prod in listOfProducts)
{
if (listOfResults.Contains(prod.Name.ToLower()) == false)
listOfResults.Add(prod.Name.ToLower());
}
return listOfResults;
}
I'd like to pass in a class variable (Like LINQ?)
public List<string> GetListOfDifferentVariables(variableType)
{
List<string> listOfResults = new List<string>();
foreach (Product prod in listOfProducts)
{
if (listOfResults.Contains(prod.variableType.ToLower()) == false)
listOfResults.Add(prod.variableType.ToLower());
}
return listOfResults;
}
example Usage:
ProductList.GetListOfDifferentVariables(o => o.Name);
Sample input (Variable string Name)
Apple
Apple
Apple
Pear
Banana
Banana
Output
apple
pear
banana
mvc
class Product
{
public string Name;
public string Manufacturer;
public string Description;
public string Location;
}
class ProductCollection
{
List<Product> listOfProducts;
public List<string> GetListOfDifferentProducts()
{
List<string> listOfResults = new List<string>();
foreach (Product prod in listOfProducts)
{
if (listOfResults.Contains(prod.Name.ToLower()) == false)
listOfResults.Add(prod.Name.ToLower());
}
return listOfResults;
}
}
If you are looking for the distinct set of names, consider:
listOfProducts.Select(z => z.Name.ToLower()).Distinct();
Similarly for variableType:
listOfProducts.Select(z => z.variableType.ToLower()).Distinct();
This will avoid you needing to write explicit GetListOfDifferentVariables etc methods.
If you really want those methods, try something like:
public List<string> GetDistinctNames()
{
return GetDistinctProperties(product => product.Name.ToLower());
}
public List<string> GetDistinctProperties(Func<Product, string> evaluator)
{
return listOfProducts.Select(evaluator).Distinct().ToList();
}
whereby GetDistinctNames passes in the particular field (i.e. Name) and manipulation (i.e. ToLower) it is interested in.
string[] tmp =
{
"Apple",
"Apple",
"Apple",
"Pear",
"Banana",
"Banana"
};
string[] tmp2 =
{
"Pear",
"Banana"
};
IEnumerable<string> uniqueList = tmp.Distinct(); // Removes doubles
IEnumerable<string> notInTmp2 = tmp.Distinct().Except(tmp2); // could be also interesting ;)
Console.WriteLine(string.Join(", ", uniqueList));
Console.WriteLine(string.Join(", ", notInTmp2));
If you want to pass the Property name as a parameter then you can use Reflection.
You will need to slightly tweak your Product class to have public properties.
public class Product
{
private string _name, _manufacturer;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public string Manufacturer
{
get
{
return _manufacturer;
}
set
{
_manufacturer = value;
}
}
}
Then you can write your GetListOfDifferentVariables function as
public List<string> GetListOfDifferentVariables(string propertyName)
{
List<string> listOfResults = new List<string>();
foreach (Product prod in listOfProducts)
{
string propertyValue = (string)prod.GetType().GetProperty(propertyName).GetValue(prod);
if (listOfResults.Contains(propertyValue.ToLower()) == false)
listOfResults.Add(propertyValue.ToLower());
}
return listOfResults.Distinct().ToList();
}
you can call the function using the name of the property
var names = GetListOfDifferentVariables("Name");
var manufacturers = GetListOfDifferentVariables("Manufacturer");
Create general method:
public IEnumerable<T2> GetListOfDifferent<T1, T2>(Func<T1,T2> selector, IEnumerable<T1> inputs)
{
var result = inputs.Select(selector).Distinct();
return result;
}
public IEnumerable<T2> GetListOfDifferent<T1, T2>(string propertyName, IEnumerable<T1> inputs)
{
var paramExp = Expression.Parameter(typeof(T1), "p1");
var member = Expression.PropertyOrField(paramExp, propertyName);
var lamdaExp = Expression.Lambda<Func<T1, T2>>(member, new[] { paramExp });
var selector = lamdaExp.Compile();
var result = GetListOfDifferent(selector, inputs);
return result;
}
Usage:
var result =GetListOfDifferent<Product,string>(product => product.Name, items)
or
var result = GetListOfDifferent<Product,string>(nameof(Product.Name), items);

Check if a list contains all of another lists items when comparing on one property

I am learning Linq and I have two object lists. I want to compare one of these lists against the other to see if all of one of the properties of the objects within it can be matched to those in the other list.
So, I provide code for that but I want to change it to Linq expressions.
var list1 = new List<Product>
{
new Product{SupplierId = 1,ProductName = "Name1"},
new Product{SupplierId = 2,ProductName = "Name2"},
new Product{SupplierId = 3,ProductName = "Name3"},
new Product{SupplierId = 4,ProductName = "Name4"}
};
var list2 = new List<Product>
{
new Product {SupplierId = 1,ProductName = "Name5"},
new Product {SupplierId = 4,ProductName = "Name6"}
};
private static bool CheckLists(List<Product> list1, List<Product> list2)
{
foreach (var product2 in list2)
{
bool result = false;
foreach (var product in list1)
{
if (product.SupplierId == product2.SupplierId)
{
result = true;
break;
}
}
if (!result)
{
return false;
}
}
return true;
}
How can I do it using LINQ?
bool existsCheck = list1.All(x => list2.Any(y => x.SupplierId == y.SupplierId));
would tell you if all of list1's items are in list2.
You want to check whether there are any IDs in list1 that aren't in list2:
if (list1.Select(p => p.SupplierId).Except(list2.Select(p => p.SupplierId)).Any())
To see that list1 contains all of list2, check if Any of list1 matches All of list2:
private static bool CheckLists(List<Product> list1, List<Product> list2) => list2.All(l2p => list1.Any(l1p => l1p.SupplierId == l2p.SupplierId));
However, I would probably write an generic extension method:
public static class Ext {
static public bool ContainsAll<T, TKey>(this List<T> containingList, List<T> containee, Func<T, TKey> key) {
var HSContainingList = new HashSet<TKey>(containingList.Select(key));
return containee.All(l2p => HSContainingList.Contains(key(l2p)));
}
static public bool ContainsAll<T>(this List<T> containingList, List<T> containee) => containingList.ContainsAll(containee, item => item);
}
Then you can call it like:
var ans = list1.ContainsAll(list2, p => p.SupplierId);

Combine/Merge 3 lists of objects based on condition

public class Derp
{
public Derp
{
listOfStrings = new List<string>();
}
public string strName;
public List<string> listOfStrings;
public int unrequiredInt;
public bool unrequiredBool;
}
List<Derp> derp1 = ... //generate data assume strName is unique in list, but not across lists;
List<Derp> derp2 = ... //generate data;
List<Derp> derp3 = ... //generate data;
List<Derp> mergedDerp = new List<Derp>();
I need to merge derp1 and derp2 and derp3 with the condition derp1[x].strName == derp2[y].strName == derp3[z].strName. The merged list should have all Derps but merge derp1,2,3 into one derp based on the condition above (unrequiredInt and unrequiredBool's content doesn't matter). I know it can be done in LINQ but I'm quite at a loss. Something like ...
mergedDerp = derp1.Join(derp2, d1 => derp1, d2 => derp2, (d1,d2) => new { ... ;
//and the next derp would be (i assume)
mergedDerp = mergedDerp.Join(derp3, md => mergedDerp, ...;
But i'm not getting it.
The result should contain a list of unique Derps by their strName, and if any Derps were merged, the listOfStrings should all be appended into the new Derp.
Using GroupBy instead of Join seems more suitable in your case:
var mergedDerp = derp1.Union(derp2).Union(derp3).GroupBy(x => x.strName)
.Select(x => new Derp
{
strName = x.Key,
// I guess you want to merge the list of strings as well?
listOfStrings = x.SelectMany(d => d.listOfStrings).ToList()
// Leave unrequired fields as default or just use the first derp's value
// unrequiredInt = x.First().unrequiredInt,
// unrequiredBool = x.First().unrequiredBool,
})
.ToList();
It sounds like you want to determine equality based on the strName value. If so, simply implement the Equals and GetHashCode methods on the object:
public class Derp
{
public Derp()
{
listOfStrings = new List<string>();
}
public string strName;
public List<string> listOfStrings;
public int unrequiredInt;
public bool unrequiredBool;
public override bool Equals(object obj)
{
return ((Derp) obj).strName.Equals(strName);
}
public override int GetHashCode()
{
return strName.GetHashCode();
}
}
Then when you combine them, you can just use Union and Distinct:
var derp1 = new List<Derp>();
derp1.Add(new Derp() {strName = "X"});
derp1.Add(new Derp() { strName = "Y" });
derp1.Add(new Derp() { strName = "Z" });
var derp2 = new List<Derp>();
derp2.Add(new Derp() {strName = "A"});
derp2.Add(new Derp() { strName = "B" });
derp2.Add(new Derp() { strName = "X" });
var derp3 = new List<Derp>();
derp3.Add(new Derp() { strName = "J" });
derp3.Add(new Derp() { strName = "B" });
derp3.Add(new Derp() { strName = "X" });
var merged = derp1.Union(derp2.Union(derp3)).Distinct();
Console.WriteLine(merged.Count()); // Returns 6: X, Y, Z, A, B, J

Compare 2 Lists and Create New List with Different Items

I have two Lists that I'm trying to compare. What I need to accomplish is essentially to remove the items that are duplicated between the two lists and only keep the objects that are different. Right now, I'm inserting the non-duplicate data into a new list.
The data I'm using here...
LIST1
("b",2)
("c",3)
LIST2
("a",1)
("b",2)
("c",3)
("d",4)
NEWLIST
("a",1)
("d",4)
Here's what I have so far...
My object:
public class TestClass
{
protected string teststring;
protected int testint;
public string TestString
{
get { return teststring; }
set { teststring = value; }
}
public int TestInt
{
get { return testint; }
set { testint = value; }
}
public TestClass() { }
}
My compare logic:
private static List<TestClass> CompareCodes(List<TestClass> list1, List<TestClass> list2)
{
List<TestClass> newList = new List<TestClass>();
foreach (TestClass s in list2)
{
if (list1.Contains(s) == false)
{
newList.Add(s);
}
}
if (newList.Count != 0)
return newList;
else
return null;
}
The new list will be used to insert data into a database table. If it's null, no action will be taken. I'm using .NET 2.0 in this app (it's an enhancement to an older app), so I can't use LINQ. So is there any other way to make this work that I'm missing? Or is there a better way to do this? I haven't been able to find anything (maybe just not looking hard enough) to accomplish what I'm trying to do.
Thanks in advance!
So you're almost there, but you'll need to override the Equals method on your class to make it work:
public class TestClass
{
public override bool Equals(object y)
{
TestClass newY = y as TestClass;
if (newY == null) { return false; }
return newY.TestString == this.TestString &&
newY.TestInt == this.TestInt;
}
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + this.TestInt.GetHashCode();
hash = hash * 23 + this.TestString == null ?
0 :
this.TestString.GetHashCode();
return hash;
}
}
}
Use Jon Skeet's answer here to implement your hash code.
In the code that you have provided you are only keeping the items of list2 that are not in list1. but how about the items that are in list1 but not in list2 ? and since you mention
What I need to accomplish is essentially to remove the items that are duplicated between the two lists and only keep the objects that are different
This code belows returns a new list with the items that are unique in both lists
private static List<TestClass> CompareCodes(List<TestClass> list1, List<TestClass> list2)
{
List<TestClass> newList ;
newList = new List<TestClass>();
//All the items in list1 that are not in list2 are added
foreach (TestClass s in list1)
{
if ( ! list2.Contains(s))
{
newList.Add(s);
}
}
//All the items in list2 that are not in list1 are added
foreach (TestClass s in list2)
{
if ( ! list1.Contains(s))
{
newList.Add(s);
}
}
return newList;
}
And in your class
public class TestClass implements IEquatable
{
protected string teststring;
protected int testint;
public string TestString
{
get { return teststring; }
set { teststring = value; }
}
public int TestInt
{
get { return testint; }
set { testint = value; }
}
public override bool Equals(object y)
{
TestClass newY = y as TestClass;
if (newY == null) { return false; }
return newY.TestString == this.TestString &&
newY.TestInt == this.TestInt;
}
public override int GetHashCode()
{
// use this example or implement some hash code logic
return this.TestInt.GetHashCode() ;
}
public TestClass() { }
}
private static List<TestClass> CompareCodes(List<TestClass> list1,
List<TestClass> list2)
{
List<TestClass> newList = new List<TestClass>();
bool found = false;
foreach (TestClass s in list2)
{
foreach (TestClass t in list1)
{
//let's say that teststring is your object id / key
if(s.TestString==t.TestString )
{
found = true;
break;
}
}
if(!found)
newList.Add(s);
found=false;
}
// do the something for the List1
foreach (TestClass s in list1)
{
foreach (TestClass t in list2)
{
//let's say that teststring is your object id / key
if(s.TestString==t.TestString )
{
found = true;
break;
}
}
if(!found)
newList.Add(s);
found=false;
}
if (newList != null)
return newList;
else
return null;
}

Do a correct union on two List<SelectListItem>

retval.AddRange(oldList.Union(newList));
Both the oldList and newList are declared here
List<SelectListItem> oldList = new List<SelectListItem>();
List<SelectListItem> newList = new List<SelectListItem>();
I want to union these two lists, removing duplicate items that have the same "text" property. If there is a dupe between newList and oldList on the text property, it should keep the text/value pair of the newList.
The above union doesn't seem to be doing anything besides concat'ing the lists almost, don't know why.
What am I doing wrong?
I want to union these two lists, removing duplicate items that have the same "text" property.
The Union method will not handle this requirement. You could do this via:
retVal.AddRange(newList); // Since you want to keep all newList items, do this first
// Add in all oldList items where there isn't a match in new list
retVal.AddRange(oldList.Where(i => !newList.Any(n => i.Text == n.Text)));
From reading the documentation it appears that A.Union(B) in effect just adds to A those items from B that aren't already in A. That is:
When the object returned by this
method is enumerated, Union enumerates
first and second in that order and
yields each element that has not
already been yielded.
A quick test bears that out. So oldList.Union(newList) will keep the old values whereas newList.Union(oldList) will give you the new values.
Here's my test code:
class MyThing
{
public string Text { get; private set; }
public int Version { get; private set; }
public MyThing(string t, int v)
{
Text = t;
Version = v;
}
public override int GetHashCode()
{
return Text.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
MyThing other = obj as MyThing;
if (other == null)
return false;
return this.Text.Equals(other.Text);
}
}
static List<MyThing> oldList = new List<MyThing>()
{
new MyThing("Foo", 0),
new MyThing("Bar", 0),
new MyThing("Fooby", 0),
};
static List<MyThing> newList = new List<MyThing>()
{
new MyThing("Barby", 1),
new MyThing("Bar", 1)
};
static void DoIt()
{
var unionOldNew = oldList.Union(newList);
Console.WriteLine("oldList.Union(newList)");
foreach (var t in unionOldNew)
{
Console.WriteLine("{0}, {1}", t.Text, t.Version);
}
Console.WriteLine();
var unionNewOld = newList.Union(oldList);
Console.WriteLine("newList.Union(oldList)");
foreach (var t in unionNewOld)
{
Console.WriteLine("{0}, {1}", t.Text, t.Version);
}
}

Categories

Resources