I'm trying to find all the duplicates in my ObservableCollection, using Lambda/LINQ
Say I have my ObservableCollection<Student> studentList
Each Student has fields
Name
Age
Class
Now in the list, say the following is input in the list
studentList.Add(new Student()
{
Name = Name,
Age= Age,
Class = Class
});
Each time the user enters the data and clicks the button, the data is added. So in my list I would have (as an example)
Bob, 6, Blue Class
Jerry 8, Red Class
Andy 7, Red Class
Bob, 10, Red Class
I would like to loop through the list and find out which names are duplicates
So far my code is:
bool cont = false;
foreach (var dup in studentList)
{
if (!studentList.Any(x => x.Name== dup.Name))
{
cont = true;
}
else
{
cont = false;
break;
}
}
But something is not right. It will always "Bob" in the if statement.
And if I entered 4 completley different names, it would still say false. It takes the last entered value and always compares it to that one, never actually checking them all.
It is a bit hard to explain. If I knew where the problem lie, I would be of more help.
All I would like is to loop through my list to find matching values, and to set the bool to false and break the loop, so that the user can be notified and can make changes before they continue. And I would like the solution to be in a Lambda/LINQ statement
studentList.GroupBy(n => n.Name).Where(n => n.Count() > 1).Select(n => n.Key);
Explaination: This code first groups them by names creating a dictionary in form name => list of students with that name, then checks where there are more than one name and selects that names.
How about this:
bool anyDuplicates = studentList.Select(i => i.Name).Distinct().Count()
< studentList.Count();
This checks if the Distinct list of names is smaller than the full list of students.
According to your comment you don't need a foreach loop you can do this in one line:
bool control = studentList
.Any(s => studentList.Count(x => x.Name == s.Name) > 1);
EDIT
You can simply use the linq like below:
var duplicateNamesList = studentList.GroupBy(x => x.Name).Where(x => x.Count() > 1).Select(x => x.Key);
You have to implement IEqualityComparer<T> for your Student class like below:
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public string Class { get; set; }
public class EqualityComparer : IEqualityComparer<Student>
{
public bool Equals(Student x, Student y)
{
return x.Name == y.Name;
}
public int GetHashCode(Student obj)
{
unchecked // overflow is fine
{
int hash = 17;
hash = hash * 23 + obj.Name.GetHashCode();
return hash;
}
}
}
}
Then just invoke studentList.Distinct(new Student.EqualityComparer()) and assign it back to studentList like below:
studentList = studentList.Distinct(new Student.EqualityComparer()).ToList();
Related
public interface IPerson
{
int Id { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
string Address { get; set; }
}
List<Customer> Customers = new List<Customer>();
private void button3_Click(object sender, EventArgs e)
{
int b = Convert.ToInt32(comboBox1.Text);
foreach (IPerson person in Customers)
if (person.Id == b)
Customers.Remove((Customer)person);
}
I want to delete the customer data kept in this way according to the id information selected from the combobox that appears in the visual. what should I do ?
List<T> has a RemoveAll method that accepts a predicate:
Customers.RemoveAll( c => b == c.Id );
First of all, you don't need to foreach an IPerson - you already know it's a customer - since you have a list of Customers, so why bother with the interface and why even specify it?
Second of all - you could remove all the items matching a pattern with RemoveAll ; code below is a bit verbose, but I will explain after.
var listOfCustomers = new List<Customer>();
listOfCustomers.Add(new Customer() { ID = 1 });
listOfCustomers.Add(new Customer() { ID = 2 });
listOfCustomers.RemoveAll(matchingCustomer => matchingCustomer.ID == 1);
Console.WriteLine(listOfCustomers.Count());
For this example I add 2 customers, Customer 1 and Customer 2
Normally I wouldn't use matchingCustomer as a term, but this is for the explanation; you tell the Lambda function: "I will use 'matchingCustomer' as the name you should be using while performing this action".
You could even chain this kind of functions, like so:
matchingCustomer.ID == id &&
matchingCustomer.Status = CustomerStatus.Active &&
matchingCustomer.SignOnDate < DateTime.Now.AddYears(-1)
So you say - the ID should be the id I got in the function, but it should also be active and at least signed on 1 year ago. (Where CustomerStatus is an enum)
You can do it using the following this way.
List<string> MyList = new List<string>();
MyList.Add("A");
MyList.Add("B");
MyList.Add("C");
MyList.Add("D");
MyList.Add("E");
MyList.ForEach(i => Console.Write("{0}\t", i));
//You should write the index of the element you want to delete here.
MyList.RemoveAt(1);
MyList.ForEach(i => Console.Write("{0}\t", i));
I have a list of objects ListA with property Id and I have to make a query in a table that has a column Id and find the rows that the ids are the same. How exactly can I achieve that with a single query and not a foreach loop of listA?
Thank you for your time
foreach(var object in listA)
{
context.Table.Where(x => x.id == object.Id)....
}
Looks like you want to return all rows from the table that have an ID contained in the list of objects with the same ID. The following will achieve this. I can modify my answer to suit your need. Just let me know if you are looking for something slightly different.
void Main()
{
var listA = new List<A> { new A { Id = 1 }, new A { Id = 4 } };
var results = context.Table
.Where(t => listA.Select(l => l.Id).Contains(t.Id))
}
public class A
{
public int Id { get; set; }
}
I have a following data structure:
Dictionary<int, List<Home>> localDictionary;
Dictionary key here is the CityId of int type
public class Home
{
public int Id;
public string Address;
public string Color;
}
//Following is the Output entity:
public class HomeOutput
{
public int CityId;
public int Id;
public string Address;
public string Color;
}
Final Result in the form of the collection of HomeOutput type will require flattening of the structure, since for every city, it needs to iterate over home list and fill the HomeOutput object
I can do it easily using the foreach loop by first iterating over keys then List<Home> and adding all the details to the HomeOutput collection, but not able to get the correct answer using Linq SelectMany, following is what I have tried:
List<HomeOutput> homeOutputList =
localDictionary.SelectMany(
x => x.Value.SelectMany<Home, HomeOutput>(
y => new HomeOutput()
{
CityId = x.Key,
Id = y.Id,
Address = y.Address,
Color = y.Color
}))
.ToList();
Error is I am trying to convert HomeOutput to IEnumerable<HomeOutput>, but what shall I modify to ensure that I can get the collection with correct result.
I believe your LINQ might be slightly off. This should work.
List<HomeOutput> homeOutputList =
localDictionary.SelectMany(
x => x.Value.Select(
y => new HomeOutput()
{
CityId = x.Key,
Id = y.Id,
Address = y.Address,
Color = y.Color
}))
.ToList();
I am trying to use Group By method supported by LINQ.
I have this class
public class Attribute
{
public int Id {get;set;}
public string Name {get;set;}
public string Value {get;set;}
}
I have a service method that will retrive a IList
var attributes = _service.GetAll();
Id Name Value
7 Color Black
7 Color White
220 Size 16
Now I have another tow classes
one is
public class AttributeResourceModelSubItem
{
public string Name {get;set;}
public List<AttributeValueResourceModelSubItem> values { get; set; }
}
public class AttributeValueResourceModelSubItem
{
public int Id;
public string Name {get;set;}
}
I am trying to loop through the attributes list. and if the attribute id is the same, I wanna insert the records where id = to that id inside the AttributeValueResourceModelSubItem in which id = 1 and Name will be equal to the attribute value.
This what I got so far.
private IList<AttributeResourceModelSubItem> FormatAttributes(IList<Attribute> attributes)
{
Dictionary<int, Attribute> baseTypes = new Dictionary<int, Attribute>();
AttributeResourceModelSubItem attributeResourceModelSubItem = null;
var list = new IList<AttributeResourceModelSubItem>();
foreach (var item in attributes)
{
if (!baseTypes.ContainsKey(item.Id))
{
attributeResourceModelSubItem = new AttributeResourceModelSubItem()
attributeResourceModelSubItem.key = item.Name;
attributeResourceModelSubItem.values.Add(new AttributeValueResourceModelSubItem()
{
id = 1,
name = item.Value
});
list.Add(attributeResourceModelSubItem);
}
baseTypes.Add(item.Id, item);
}
return list;
}
Any help is appreciated.
It's pretty unclear from your example what you're actually trying to do, but this is the gist I get.
private IEnumerable<AttributeResourceModelSubItem> FormatAttributes(IEnumerable<Attribute> attributes)
{
return attributes.GroupBy(c => c.Id)
.Select(c => new AttributeResourceModelSubItem()
{
key = c.First().Name,
values = c.Select(x => new AttributeValueResourceModelSubItem()
{
id = 1,
name = x.value
}).ToList();
});
}
You should also definitely not use the word Attribute as a class name. That's already a .NET class.
I'll admit that I don't quite understand the id = 1 part, but I took that from your code. It also seems odd to group by the id then try and take the first name, but again that's what you have.
If you do, in fact, want to group by the name and take the id, which makes a little more sense, you'll want to swap a couple things around. Admittedly this structure still seems a little odd to me, but hopefully this will get you a couple steps closer to your goal.
private IEnumerable<AttributeResourceModelSubItem> FormatAttributes(IEnumerable<Attribute> attributes)
{
return attributes.GroupBy(c => c.name)
.Select(c => new AttributeResourceModelSubItem()
{
key = c.Key,
values = c.Select((item, index) => new AttributeValueResourceModelSubItem()
{
id = index + 1,
name = item.value
}).ToList();
});
}
I also made your id = 1 increment starting at one for each element in each values list. You might want that to be item.Id, or even just your original 1.
I have this class:
public class Document
{
public string ID { get; set; }
public string Type { get; set; }
public bool Checked {get;set; }
}
I create a set of 10 elements using Enumerable.Repeat static method:
var list = Enumerable.Repeat<Document>(
new Document
{
ID="1",
Type ="someType"
Checked = true
}, 10).ToList<Document>();
These creates 10 Documents all with the same properties. I need that some of them, for instance, the first 5 elements of the list list have the Checked property to false.
How can I achieve it, using as possible linq?
Note that your original sample has a bug because it's creating a 10 element List<Document> that only has 1 actual Document object. Here is a better way of doing it
Enumerable
.Range(1, 10)
.Select(i =>
new Document() {
ID = "1",
Type = "someType",
Checked = i <= 5
})
.ToList();
EDIT
Changed the code to be simpler. My original response was to editing an already existing list for which the following can be done
list.Take(5).ForEach(x => { x.Checked = false });
Note that you may have to define a simple ForEach method for this operation. If you don't have one defined here is an example
static class Extensions {
internal static void ForEach<T>(this IEnumerable<T> e, Action<T> action) {
foreach (var item in e) {
action(item);
}
}
}
Alternate idea to accomplish what you're asking for (also populates your ID column with something other than "1"):
var list = Enumerable.Range(1, 10)
.Select(i => new Document
{
ID = i.ToString(),
Type = "someType",
Checked = (i > 5)
}).ToList();