How to iterate over all entries in a single GridView's column? - c#

Consider the following grid view:
Its fairly empty at the moment but will be more populated.
peronID column will always be in the same place. How can I iterate over the first column, counting all personID's equal to a given amount? Something like this (pseudocode) :
foreach element in personID
if element == 25
count += 1
Code that populates the GridView:
private void DisplayTable()
{
LibraryEntities1 myDB = new LibraryEntities1();
var people = myDB.People;
gridViewPeople.DataSource = people.ToList();
gridViewPeople.DataBind();
}

I suggest you operate on the data, not on the grid, so assuming the List below is what you might have bound to your grid:
Given class Person:
class Person
{
public int PersonID { get; set; }
public string PersonName { get; set; }
}
You can group by and sum just like you might do in SQL:
List<Person> persons = new List<Person>{
new Person{ PersonID = 13, PersonName = "Foo" },
new Person{ PersonID = 13, PersonName = "Foo" },
new Person{ PersonID = 15, PersonName = "Foo" }
};
var grp = persons.GroupBy(p => p.PersonID)
.Select(p => new {pid = p.Key, count = p.Count()});
foreach (var element in grp)
{
Console.WriteLine($"PersonID = {element.pid}, Count of Person = {element.count}");
}
Output:
PersonID = 13, Count of Person = 2
PersonID = 15, Count of Person = 1

Related

How to increment an integer property for every class inside List in c#

I am having a class as below:
public class DBEmployee
{
public Guid EmpId {get;set;}
public string Name {get;set;}
public int IncrementNo {get;set;}
}
public class C#Employee
{
public Guid EmpId {get;set;}
public string Name {get;set;}
public int IncrementNo {get;set;}
}
Consider I got List of Employees from database and bind it to the variable listOfEmployees as below:
var listOfEmployees = new List<DBEmployee>
{
new DBEmployee {
EmmId = "e31d712a-7d5c-4b1c-99a8-306f9aebbfa0",
Name = "AAA",
IncrementNo = 0
},
new DBEmployee {
EmmId = "87c4feed-20e7-42b2-8a95-8acb0743fdfe",
Name = "BBB",
IncrementNo = 0
},
new DBEmployee {
EmmId = "90cdd219-8796-4d51-bfe1-add8515315c5",
Name = "CCC",
IncrementNo = 0
},
}
Now I am using AutoMapper to map DBEmployee to C#Employee as below:
var config = new MapperConfiguration(cfg => cfg.CreateMap<DBEmployee, C#Employee>()
.AfterMap((src, dest) => dest.IncrementNo= dest.IncrementNo+ 1)
);
var mapper = new Mapper(config);
var c#Emps = mapper.Map<List<C#Employee>>(listOfEmployees);
I tried in above format but it's always getting IncrementNo as 1 for every Employee.
Now I want the IncrementNo property to be increment by +1 for every employee object such as
Employee Name AAA should contain IncrementNo=1 and Employee Name BBB should contain IncrementNo=1 and Employee Name CCC should contain IncrementNo=3.
I can acheieve this by using Foreach loop as below:
var lR = 0;
c#Emps.ForEach(x =>
{
x.IncrementNo= lR + 1;
lR++;
});
but i just want to try in a better way mostly while configuring AutoMapper itself rather that having more lines of code using ForEach
Could someone help me with this?
Have you tried this?
Mapper.CreateMap<DBEmployee, C#Employee>()
.AfterMap((src, dest) =>
{
int incrementNo = 1;
foreach (var employee in listOfEmployees)
{
employee.IncrementNo = incrementNo++;
}
});
Sorry please ignore typing mistakes as I have not compiled the code. But conceptually it should be similar.

Add element to a List when delcaring new object

Is there a way to add elements to a List when doing this:
var Foo = new MyClass() {
PropertyList = MyList,
Id = Id,
}
I would like to add elements to PropertyList. For example would be the same as: MyList.Add()
The problem is that i do not have a list called MyList but i rather have elements that i want to append to PropertyList
Updating code based on comments:
var result1 = await query
.GroupBy(c => new {
c.CommissionId, c.ActivityId
})
.Select(grp => new RegistrationStatisticViewModel() {
CommissionId = grp.Key.CommissionId,
CommissionCode = grp.First().Commission.Code,
CommissionDescription = grp.First().Commission.Description,
MinuteWorked = grp.Sum(c => c.MinuteWorked),
ActivityId = grp.Key.ActivityId,
ActivityCode = grp.First().Activity.Code,
ActivityDescription = grp.First().Activity.Description,
})
.ToListAsync();
var grps = from d in result1
group d by d.CommissionId
into grp
select new RegistrationStatisticViewModel() {
CommissionId = grp.Key,
ActivityList = new List < Activity > {
new Activity {
//ActivityId = grp.Select(d => d.ActivityId),
//Code = grp.Select(d => d.ActivityCode),
//Description = grp.Select(d => d.ActivityDescription),
}
},
CommissionCode = grp.First().CommissionCode,
CommissionDescription = grp.First().CommissionDescription,
MinuteWorked = grp.First().MinuteWorked
};
return grps;
To give context:
forget the result1 is just some data i retrieve from my database
Commission is one class and contains:
CommissionId
Code
Description
Activity is one class and contains:
ActivityId ==> type GUID
Code ==> type string
Description ==> type string
Now the var = grps is a LINQ that gets the data and then instatiates a new object (class) new RegistrationStatisticViewModel()
So the tricky part we were discussing before is when i populate ActivityList with multiple activities.
When populating the list if i use .First() or .Select() i would only get one instance and therfore the list would only have one activity.
It worked when using .ToArray() for example if i replace ActivityList with just the ActivityId of type string (so a new property on RegistrationStatisticViewModel that is not a list anymore):
I can do this ActivityId = grp.Select(d2 => d2.ActivityId).ToArray()
And it will give me an array of all the ActivityId linked to that commissionId
I am sorry if this is confusing but it is for me as well. I would thank you if you could help me. No worries if you can't you have already give me very helpful answers, so i thank you for that!
Based on your remarks, I believe this is what you are trying to achieve:
public class PersonContainer
{
public IList<Person> Persons { get; set; }
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
var personContainer = new PersonContainer
{
Persons = new List<Person>
{
new Person
{
Name = "John Doe",
Age = 28,
},
new Person
{
Name = "Jane Doe",
Age = 27,
},
}
};
Here, the Persons property of PersonContainer is initialized and populated with Person elements during instantiation.

Nest foreach cycle and adding items to collection into linq methods

I have the following classes:
class Person
{
public string Name { get; set; }
}
class Student
{
public string Name { get; set; }
}
I have a list of persons:
IList<Person> persons = new List<Person>
{
new Person() { Name = "Bill" },
new Person() { Name = "Bob" },
new Person() { Name = "Henry" },
};
And I adding items to new collection in foreach loop:
IList<Student> students = new List<Student>();
//Is it possible to nest the following rows in linq method?
foreach (var person in persons)
{
students.Add(new Student() { Name = person.Name });
}
Is it possible to nest foreach and adding items into linq method?
If you mean replacing the foreach loop with a LINQ query (not quite sure why you are talking about nesting loops in your question) then you could try this:
IList<Student> students = persons.Select(p => new Student { Name = p.Name }).ToList();
or if you prefer:
IList<Student> students = (from p in persons select new Student { Name = p.Name }).ToList();

Getting repeated data from a CSV file

I have a person class like so:
class Person
{
string Id { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
}
There is a CSV file which has person data like
"123","ABC","DEF"
"456","GHI","JKL"
"123","MNO","PQR"
...
A person is unique based on the Id.
The CSV is read like this:
using (StreamReader sr = new StreamReader(inputFile))
{
string[] arrCsvData;
string strLine;
while ((strLine = sr.ReadLine()) != null)
{
arrCsvData = strLine.Split(',');
this.LoadPersonData(arrCsvData);
}
}
In LoadPersonData a new Person object is created and assigned the values from CSV:
Person objPerson = new Person();
for (int i = 1; i <= arrCsvData.Length - 1; i++)
{
// Assign person property values from arrCsvData
}
I have a dictionary object in which the key is the ID and the value is the Person object.
if(!this.PersonDataCollection.ContainsKey(personKey))
{
this.PersonDataCollection.Add(objPerson);
}
This gives me all the unique Person objects from the CSV file.
I want to create a list of those Person objects which are repeated based on Id in the CSV.
So the list DuplicatePersons will have:
"123","ABC","DEF"
"123","MNO","PQR"
in it.
The bare bones way is to first read all the person objects into a list and then do a LINQ query to get all the duplicates in a separate list. This way I have to create an extra collection just to get the duplicates.
There should be a better way than creating a separate list.
Any pointers?
First of all, I would use LINQToCSV. Parsing CSV files is more complicated than just splitting by ,. You don't need to code anything, just create your class, and place attributes on it:
class Person
{
[CsvColumn(Name = "ID", ...)]
string Id { get; set; }
[CsvColumn(Name = "First Name", ...)]
string FirstName { get; set; }
[CsvColumn(Name = "Last Name", ...)]
string LastName { get; set; }
}
Then when you read the file using LINQToCSV, you get an IEnumerable<Person>... and then you can do:
IEnumerable<Person> people = ... //read here using LINQToCSV
var grouped = people.GroupBy(p => p.Id);
If you will know the the unique column at runtime, you can do something like this:
string columnName = "Id";
persons.GroupBy(x => x.GetType().GetProperty(columnName).GetValue(x, null));
although you will have to see how much it affects you in performance. Another way that doesn't require Reflection could be:
Dictionary<string, Func<Person, object>> selectors = new Dictionary <string, Func<Person, object>>
{
{"Id", x => x.Id},
{"FirstName", x => x.FirstName},
{"LastName", x => x.LastName},
};
string columnName = "Id";
var grouped = people.GroupBy(selectors[columnName]);
Now, using your approach... what's wrong with creating another dictionary?
You could have something like:
//Here you will store the duplicated person
//Key: The person Id
//Value: The list of person sharing that same Id
Dictionary<string, IList<Person>> duplicatedPeople;
if(!this.PersonDataCollection.ContainsKey(personKey))
{
this.PersonDataCollection.Add(objPerson);
}
else
{
//Here we store all the people with this already existing ID
IList<Person> duplicatedPeople;
//If we already have this ID in the dictionary of repeated people
if (this.duplicatedPeople.TryGetValue(personKey, out duplicatedPeople)) {
//Just add this new person
duplicatedPeople.Add(objPerson);
}
//If this is the 1st time we found a duplicated person with this ID
else {
//We add two persons to the list: this one, and the one from PersonDataCollection.
duplicatedPeople = new List<Person> { this.PersonDataCollection[personKey], objPerson };
//Add it to the dictionary
this.duplicatedPeople.Add(personKey, duplicatedPeople);
}
}
Why Don't you check whether the values are already exist at this point.
Person objPerson = new Person();
for (int i = 1; i <= arrCsvData.Length - 1; i++)
{
// Assign person property values from arrCsvData
}
Check your condition here and do what ever you want with the duplicate values at that point.
Whatever you do.. there will always be a separate list. It is up to you on how you want them to come about though.
Option 1 - Temporary lists
Each time you query your existing dictionary, an in-memory result will be returned. Depending on how big your dataset is.. this may not be what you're after.
Option 2 - Static list
Why not just maintain your own list at this point?:
if(!this.PersonDataCollection.ContainsKey(personKey))
{
this.PersonDataCollection.Add(objPerson);
}
else
{
// Create a new dictionary for the duplicates
this.DuplicatePersonDataCollection.Add(objPerson);
}
Create a single list for all the persons and rather query it with LINQ to get your results:
ie:
var persons = new List<Person>();
persons.Add(new Person { Id = "123", FirstName = "AAA", LastName = "XXX" });
persons.Add(new Person { Id = "123", FirstName = "BBB", LastName = "WWW" });
persons.Add(new Person { Id = "456", FirstName = "CCC", LastName = "XXX" });
persons.Add(new Person { Id = "456", FirstName = "DDD", LastName = "YYY" });
persons.Add(new Person { Id = "789", FirstName = "EEE", LastName = "ZZZ" });
var duplicateKeys = persons.GroupBy(p => p.Id).Select(g => new { g.Key, Count = g.Count() }).Where(x => x.Count > 1).ToList().Select(d => d.Key);
var duplicatePersons = persons.Where(p => duplicateKeys.Contains(p.Id)).ToList();
var unique = persons.GroupBy(p => p.Id).ToList();

Updating list's element's properties

I have a class
class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int ID { get; set; }
}
Now, I have created a List of Student object as:
List<Student> lst = new List<Student>();
Three properties of the class will come from three different Data Sources. So I cannot add all properties of object at a time. I am adding the ID property as following:
lst.Add(new Student { ID = 1 }, new Student { ID = 2 })
Now I want to set the values of the "FirstName" or "LastName" property in the existing list. How I can do this?
List<Student> lst = new List<Student>();
lst.Add(new Student { ID = 1 });
lst.Add(new Student { ID = 2 });
//Get the student you want by id then use that to populate the remaining properties
var temp = lst.Single(l => l.ID == 1);
temp.FirstName = "fname";
temp.LastName = "lastname";
Don't mix method Add() call:
list.Add(new Student());
and collection initialization syntax:
List<Student> list = new List<Student>
{
new Student(),
new Student()
};
Then
int index = 1;
Student student = list[index]; // may cause IndexOutOfRangeException
student.Name = "Me"; // may cause NullReferenceException
or shorter:
list[i].Name = "Me"; // may cause them both too
You can get the index from an element that matches with the element to update:
int index= MyObjectList.FindIndex(x=>x.Id==object.Id);
and after updating it:
MyObjectList[index]=object;
Im only guessing here... if , doesnt work try ;
lst.Add(new Student{ID=2, FirstName="The", LastName="Stig"});
Or you can define a constructor for the Student object...
public Student(int mID, string mFirstName, string mLastName)
{
ID = mID;
FirstName = mFirstName;
LastName = mLastName;
}
Then use
lst.Add(new Student(2, "The", "Stig"));
Assuming the other data sources contains the studentId and all students has a last and first name.
Join the lastNameList and firstNameList collections using linq. The studentId list you dont need since the other two has that value also.
var list =
(from lastName in lastNameList
join firstName in firstNameList on lastName.studentId equals firstName.studentId
select new Student()
{
.ID = lastName.studentId,
.FirstName = firstName.Name,
.LastName = lastName.Name
}).ToList();
You can use list.count-1 to get the last item in the list. Then you can update the properties you want.
List<Student> lst=new List<Student>();
lst.Add(new Student { ID = 1, FirstName = "FirstName", LastName = "LastName" });
//Then get props from other datasource
lst[lst.Count-1].FirstName="FirstName";
lst[lst.Count-1].LastName="LastName";

Categories