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.
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 that I'm pulling through an API, just struggling to find a way to display the output in alphabetical order.
#foreach (_SiriServiceDeliverySituationExchangeDeliveryPtSituationElement Situation in
(Model.ServiceDelivery.SituationExchangeDelivery.Situations))
{
Alert for: #Situation.Summary.Value
}
The situation.summary.value displays multiple names of a places in a list and at the moment just displays in a random order, I know I can do Enumerable.Reverse to reverse the order of the list but just can't figure out a way to do it alphabetically.
Thanks in advance for any help.
Import System.Linq and use OrderBy()
#foreach (_SiriServiceDeliverySituationExchangeDeliveryPtSituationElement Situation in
(Model.ServiceDelivery.SituationExchangeDelivery.Situations.OrderBy(s => s.Summary.Value))
{
Alert for: #Situation.Summary.Value
}
Check if it works!
Here is a simple example to order by Situations property.
Class and mocked data
public class SituationExchangeDelivery
{
public int Id { get; set; }
public string Situations { get; set; }
public override string ToString() => Situations;
}
public class Mocked
{
public static List<SituationExchangeDelivery> SituationExchangeList() =>
new List<SituationExchangeDelivery>()
{
new SituationExchangeDelivery() {Id = 1, Situations = "Newsome"},
new SituationExchangeDelivery() {Id = 2, Situations = "Emley"},
new SituationExchangeDelivery() {Id = 3, Situations = "Outwood"},
new SituationExchangeDelivery() {Id = 4, Situations = "Stourton"}
};
Usage
IOrderedEnumerable<SituationExchangeDelivery> data =
Mocked.SituationExchangeList()
.OrderBy(item => item.Situations);
foreach (var delivery in data)
{
Console.WriteLine(delivery);
}
Or
List<SituationExchangeDelivery> data =
Mocked.SituationExchangeList()
.OrderBy(item => item.Situations).ToList();
foreach (var delivery in data)
{
Console.WriteLine(delivery);
}
Output
Emley
Newsome
Outwood
Stourton
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();
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();
This is all in C#, using .NET 2.0.
I have two lists of objects. They are not related objects, but they do have certain things in common that can be compared, such as a GUID-based unique identifier. These two lists need to be filtered by another list which just contains GUIDs which may or may not match up with the IDs contained in the first two lists.
I have thought about the idea of casting each object list to just object and sorting by that, but I'm not sure that I'll be able to access the ID property once it's cast, and I'm thinking that the method to sort the two lists should be somewhat dumb in knowing what the list to be sorted is.
What would be the best way to bring in each object list so that it can be sorted against the list with only the IDs?
You should make each of your different objects implement a common interface. Then create an IComparer<T> for that interface and use it in your sort.
Okay, if you have access to modify your original classes only to add the interface there, Matthew had it spot on. I went a little crazy here and defined out a full solution using 2.0 anonymous delegates. (I think I'm way addicted to 3.0 Lambda; otherwise, I probably would've written this out in foreach loops if I was using 2005 still).
Basically, create an interface with the common properties. Make yoru two classes implement the interface. Create a common list casted as the interface, cast and rip the values into the new list; remove any unmatched items.
//Program Output:
List1:
206aa77c-8259-428b-a4a0-0e005d8b016c
64f71cc9-596d-4cb8-9eb3-35da3b96f583
List2:
10382452-a7fe-4307-ae4c-41580dc69146
97f3f3f6-6e64-4109-9737-cb72280bc112
64f71cc9-596d-4cb8-9eb3-35da3b96f583
Matches:
64f71cc9-596d-4cb8-9eb3-35da3b96f583
Press any key to continue . . .
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication8
{
class Program
{
static void Main(string[] args)
{
//test initialization
List<ClassTypeA> list1 = new List<ClassTypeA>();
List<ClassTypeB> list2 = new List<ClassTypeB>();
ClassTypeA citem = new ClassTypeA();
ClassTypeB citem2 = new ClassTypeB();
citem2.ID = citem.ID;
list1.Add(new ClassTypeA());
list1.Add(citem);
list2.Add(new ClassTypeB());
list2.Add(new ClassTypeB());
list2.Add(citem2);
//new common list.
List<ICommonTypeMakeUpYourOwnName> common_list =
new List<ICommonTypeMakeUpYourOwnName>();
//in english, give me everything in list 1
//and cast it to the interface
common_list.AddRange(
list1.ConvertAll<ICommonTypeMakeUpYourOwnName>(delegate(
ClassTypeA x) { return (ICommonTypeMakeUpYourOwnName)x; }));
//in english, give me all the items in the
//common list that don't exist in list2 and remove them.
common_list.RemoveAll(delegate(ICommonTypeMakeUpYourOwnName x)
{ return list2.Find(delegate(ClassTypeB y)
{return y.ID == x.ID;}) == null; });
//show list1
Console.WriteLine("List1:");
foreach (ClassTypeA item in list1)
{
Console.WriteLine(item.ID);
}
//show list2
Console.WriteLine("\nList2:");
foreach (ClassTypeB item in list2)
{
Console.WriteLine(item.ID);
}
//show the common items
Console.WriteLine("\nMatches:");
foreach (ICommonTypeMakeUpYourOwnName item in common_list)
{
Console.WriteLine(item.ID);
}
}
}
interface ICommonTypeMakeUpYourOwnName
{
Guid ID { get; set; }
}
class ClassTypeA : ICommonTypeMakeUpYourOwnName
{
Guid _ID;
public Guid ID {get { return _ID; } set { _ID = value;}}
int _Stuff1;
public int Stuff1 {get { return _Stuff1; } set { _Stuff1 = value;}}
string _Stuff2;
public string Stuff2 {get { return _Stuff2; } set { _Stuff2 = value;}}
public ClassTypeA()
{
this.ID = Guid.NewGuid();
}
}
class ClassTypeB : ICommonTypeMakeUpYourOwnName
{
Guid _ID;
public Guid ID {get { return _ID; } set { _ID = value;}}
int _Stuff3;
public int Stuff3 {get { return _Stuff3; } set { _Stuff3 = value;}}
string _Stuff4;
public string Stuff4 {get { return _Stuff4; } set { _Stuff4 = value;}}
public ClassTypeB()
{
this.ID = Guid.NewGuid();
}
}
}
Using only .NET 2.0 methods:
class Foo
{
public Guid Guid { get; }
}
List<Foo> GetFooSubset(List<Foo> foos, List<Guid> guids)
{
return foos.FindAll(foo => guids.Contains(foo.Guid));
}
If your classes don't implement a common interface, you'll have to implement GetFooSubset for each type individually.
I'm not sure that I fully understand what you want, but you can use linq to select out the matching items from the lists as well as sorting them. Here is a simple example where the values from one list are filtered on another and sorted.
List<int> itemList = new List<int>() { 9,6,3,4,5,2,7,8,1 };
List<int> filterList = new List<int>() { 2, 6, 9 };
IEnumerable<int> filtered = itemList.SelectMany(item => filterList.Where(filter => filter == item)).OrderBy(p => p);
I haven't had a chance to use AutoMapper yet, but from what you describe you wish to check it out. From Jimmy Bogard's post:
AutoMapper conventions
Since AutoMapper flattens, it will
look for:
Matching property names
Nested property names (Product.Name
maps to ProductName, by assuming a
PascalCase naming convention)
Methods starting with the word “Get”,
so GetTotal() maps to Total
Any existing type map already
configured
Basically, if you removed all the
“dots” and “Gets”, AutoMapper will
match property names. Right now,
AutoMapper does not fail on mismatched
types, but for some other reasons.
I am not totally sure what you want as your end results, however....
If you are comparing the properties on two different types you could project the property names and corresponding values into two dictionaries. And with that information do some sort of sorting/difference of the property values.
Guid newGuid = Guid.NewGuid();
var classA = new ClassA{Id = newGuid};
var classB = new ClassB{Id = newGuid};
PropertyInfo[] classAProperties = classA.GetType().GetProperties();
Dictionary<string, object> classAPropertyValue = classAProperties.ToDictionary(pName => pName.Name,
pValue =>
pValue.GetValue(classA, null));
PropertyInfo[] classBProperties = classB.GetType().GetProperties();
Dictionary<string, object> classBPropetyValue = classBProperties.ToDictionary(pName => pName.Name,
pValue =>
pValue.GetValue(classB, null));
internal class ClassB
{
public Guid Id { get; set; }
}
internal class ClassA
{
public Guid Id { get; set; }
}
classAPropertyValue
Count = 1
[0]: {[Id, d0093d33-a59b-4537-bde9-67db324cf7f6]}
classBPropetyValue
Count = 1
[0]: {[Id, d0093d33-a59b-4537-bde9-67db324cf7f6]}
Thist should essentially get you what you want - but you may be better of using linq
class T1
{
public T1(Guid g, string n) { Guid = g; MyName = n; }
public Guid Guid { get; set; }
public string MyName { get; set; }
}
class T2
{
public T2(Guid g, string n) { ID = g; Name = n; }
public Guid ID { get; set; }
public string Name { get; set; }
}
class Test
{
public void Run()
{
Guid G1 = Guid.NewGuid();
Guid G2 = Guid.NewGuid();
Guid G3 = Guid.NewGuid();
List<T1> t1s = new List<T1>() {
new T1(G1, "one"),
new T1(G2, "two"),
new T1(G3, "three")
};
List<Guid> filter = new List<Guid>() { G2, G3};
List<T1> filteredValues1 = t1s.FindAll(delegate(T1 item)
{
return filter.Contains(item.Guid);
});
List<T1> filteredValues2 = t1s.FindAll(o1 => filter.Contains(o1.Guid));
}
}