I feel stupid for asking this but I am struggling to quite understand foreach. Say for example that I am making a book repository app, with a Book class as well as an Inventory class. The Inventory class has a removeBook method that removes a book from the inventory. The parameter for the method would be an int bookID. I am thinking I should use foreach to accomplish this. I understand the most basic use of foreach but I can not figure out how to use it to basically select a specific bookID that is a parameter in the method. Could someone help point me in the right direction?
Here's a code snippet, I know the method is wrong:
List<Book> Books = new List<Book>
{
new Book{ bookID = 5, Name = "Moby Dick", Price = 20.00 },
new Book{ bookID = 2, Name = "50 Shades of Grey", Price = 0.99 }
};
public void removeBook(int bookID)
{
foreach (var bookID in Books)
{
Products.Remove(book);
}
}
Removing a thing from a collection isn't what foreach is for - it's for performing some operation on every value in the collection. If you want to remove a book with a specific id, you could use a regular for loop:
// in Inventory class having List<Book> Books,
// assuming Book has a public int Id property
public void RemoveBook(int bookId) {
for (int i = 0; i < this.Books.Count; i++) {
if (this.Books[i].Id == bookId) {
this.Books.RemoveAt(i);
return;
}
}
}
If there are any duplicates for some reason (there shouldn't be - IDs should be unique) and you wanted to remove all books with given ID, this code should do it:
public void RemoveBooks(int bookId) {
// iterating from the end of the array
// to prevent skipping over items
for (int i = this.Books.Count - 1; i >= 0; i--) {
if (this.Books[i].Id == bookId) {
this.Books.RemoveAt(i);
}
}
}
EDIT: fixed the code, thanks to Gerardo Grignoli
So if you want to remove a book from a list you would not necessarily use a foreach loop. Simplest way would be to use the RemoveAll function on List.
public void RemoveBook(int bookId) =>
Books.RemoveAll(book => book.Id == bookId);
If you have
List<Thing> things = ....
Then in a foreach like this
foreach (Thing theThing in things)
{
// do something with "theThing"
}
the foreach loops through all items in the things list and executes that block of code (between the { }) for each consecutive value, which is stored in the theThing variable (and is of type Thing).
You can replace Thing theThing with var theThing, for the exact same result.
foreach wont work if you are iterating over the same list so use for loop or linq instead
MSDN
The foreach statement is used to iterate through the collection to get the information that you want, but can not be used to add or remove items from the source collection to avoid unpredictable side effects. If you need to add or remove items from the source collection, use a for loop.
private void RemoveBookFromInventory(int bookID)
{
foreach(Books book in listOfBooks)
{
if(book.bookID == bookID)
{
listOfBooks.Remove(book); //wont work
}
}
for(int i=0;i<listOfBooks.Count();i++)
{
if (listOfBooks[i].bookID == bookID)
listOfBooks.Remove(listOfBooks[i]);
}
}
As others have implied, you can't really change the contents of a list while iterating through it with a for loop, as that would change the data structure you are working on.
There are several other ways to solve this though. I personally like solutions in this style, where you fetch the index of the book first, and then remove it in a separate step afterwards:
var bookID = 2;
int? index = Books
// Generate a list of book- and position- pairs:
.Select((book, pos) => new {book, pos})
// First matching pair (or null):
.FirstOrDefault(set => set.book.bookID == bookID)?
// If NULL was not returned, get the index property:
.pos;
// Removal is only attempted if a matching book was found:
if(index.HasValue){
Books.RemoveAt(index.Value);
}
Related
I have (for certain reasons not to get into now...) a List of the following structure:
List1<Dictionary1<string, List2<Dictionary2<string, string>>>>
(I added the 1 and 2 naming for clarity).
I want to iterate over List1 and sum up Dictionary1, so that all values of identical keys in Dictionary2 will add up.
For example if each Dictionary1 item contains a Dictionary2:
{ "Price", 23},
{ "Customers", 3}
then I want to iterate over all List2 elements, and over all List1 elements, and have a final dictionary of the total sum of all prices and customers as a single key for each category:
{ "Price", 15235},
{ "Customers", 236}
I hope that's clear.. In other words, I want to sum up this double-nested list in a way that I'm left with all unique keys across all nested dictionaries and have the values summed up.
I believe it can be done with LINQ, but I'm not sure how to do that..
This may be the ugliest thing I've ever written, and makes some assumptions on what you're doing, but I think this gets you what you want:
var query = from outerDictionary in x
from listOfDictionaries in outerDictionary.Values
from innerDictionary in listOfDictionaries
from keyValuePairs in innerDictionary
group keyValuePairs by keyValuePairs.Key into finalGroup
select new
{
Key = finalGroup.Key,
Sum = finalGroup.Sum(a => Convert.ToInt32(a.Value))
};
Where x is your main List.
Ok, so it looks like that you were attempting to create an Dictionary of Items with various properties (Cost, Customers, etc...), which begs the question: why not just create a class?
After all, it would be pretty simple to turn your dictionary of dictionary of items into a single dictionary of properties, such as below.
public class ItemProperties
{
public double Price {get; set;} = 0;
public int Customers {get; set;} = 0;
//Whichever other properties you were thinking of using.
}
static ItemProperties AddAll(Dictionary<string, ItemProperties> items)
ItemProperties finalitem = new ItemProperties();
{
foreach (var item in items)
{
finalitem.Price += item.Price;
finalitem.Customers += item.Customers;
//Repeat for all other existing properties.
}
return finalitem;
}
Of course, this only works if the number and kind of properties is immutable. Another way to approach this problem would be to use TryParse to find properties in Dictionary2 that you think can be added. This is problematic however, and requires some good error checking.
static Dictionary < string, string > AddNestedDictionary(Dictionary < string, Dictionary < string, string > items) {
Dictionary < string, string > finalitem = new Dictionary < string, string > ();
foreach(var item in items) {
foreach(var prop in item) {
if (!finalitem.ContainsKey(prop.Key)) {
finalitem.Add(prop);
}
double i = 0;
if (Double.TryParse(prop.Value, out i)) {
finalitem[prop.Key] += i;
}
}
}
return finalitem;
}
Again, not the best answer compared to the simplicity of a static class. But that would be the price you pay for nonclarity.
using System;
using System.Collections.Generic;
using ConsoleApp3;
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
Customers customers = new Customers();
List<Customers> names = new List<Customers>()
{
new Customers {Names = "Tanveer"},
new Customers {Names = "Nabila"},
new Customers {Names = "Suraj"}
};
foreach (int i = 0; i < names.Count; i++)
{
Console.WriteLine(customers.Names.Length);
}
}
class Customers
{
public string Names { get; set; }
}
}
}
I want to create the list of Customers and write it on the Console. But the Customers.Names is null. I am new to programming so please help.Thanks
First, let us fix up that naming. Naming is very important, but also one of the more annoying parts.
//Singular for the class
class Customer
{
//Also Singular, as this can only take 1 name
public string Name { get; set; }
}
//Plural, because it is a collection of Customer Instances.
List<Customer> Customers = new List<Customer>()
{
new Customer {Name = "Tanveer"},
new Customer {Name = "Nabila"},
new Customer {Name = "Suraj"}
};
Then you itterate over it using:
//Use a proper foreach, no need to deal with Indexes here
foreach (Customer current in Customers){
Console.WriteLine(current.Name);
}
If you do want to have a running counter, this is how the loop would look:
for(int i = 0; i < Customers.Count;i++){
//I you get any Exceptions, you want to split it up over 2 lines using a temporary variable
Console.WriteLine(Customers[i].Name);
}
What you had in your code was a bastardisation of for and foreach syntax, wich I doubt compiled.
Console.WriteLine(names[i].Names);
customers.Names is null because customers is an object which you haven't populated with any data, and has no obvious purpose. names is the actual list of customers with useful info in it.
names does not have a Names property directly either, though. The objects within the list do. So you need to refer to a specific object within the specific list.
And since you're in a loop for doing just that, names[i].Names.Length is no doubt what you intended.
N.B. However it needs to be a for rather than foreach in the loop definition - the syntax used with foreach is different. This shouldn't have compiled in order to allow you to even see the null output, so perhaps this is just a typo in your posted code.
for (int i = 0; i < names.Count; i++)
{
Console.WriteLine(names[i].Names.Length);
}
should be closer to what you need (although I'm not convinced you really are intending to print the length of each name...but that's up to you).
P.S. You should probably amend your naming convention so you've got Customer as the type and Name as the property. It's much more readable and comprehensible if they're singular rather than plural.
I'm working on creating a filter for a collection of employees. In order to do this I initially fetch a raw collection of all employees. I clone this list so I can iterate over the original list but remove items from the second list.
For each filter I have, I build a collection of employee ids that pass the filter. Having gone through all filters I then attempt to remove everything that isn't contained in any of these lists from the cloned list.
However for some reason, whenever I attempt to do this using .RemoveAll(), all records seemed to be removed and I can't figure out why.
Here is a stripped down version of the method I'm using, with only 1 filter applied:
public List<int> GetFilteredEmployeeIds(int? brandId)
{
List<int> employeeIds = GetFilteredEmployeeIdsBySearchTerm();
List<int> filteredEmployeeIds = employeeIds.Clone();
// Now filter the results based on which checkboxes are ticked
foreach (var employeeId in employeeIds)
{
// 3rd party API used to get values - please ignore for this example
Member m = new Member(employeeId);
if (m.IsInGroup("Employees"))
{
int memberBrandId = Convert.ToInt32(m.getProperty("brandID").Value);
// Filter by brand
List<int> filteredEmployeeIdsByBrand = new List<int>();
if (brandId != null)
{
if (brandId == memberBrandId)
filteredEmployeeIdsByBrand.Add(m.Id);
var setToRemove = new HashSet<int>(filteredEmployeeIdsByBrand);
filteredEmployeeIds.RemoveAll(x => !setToRemove.Contains(x));
}
}
}
return filteredEmployeeIds;
}
As you can see, I'm basically attempting to remove all records from the cloned record set, wherever the id doesn't match in the second collection. However for some reason every record seems to be getting removed.
Anybody know why?
P.S: Just to clarify, I have put in logging to check the values throughout the process and there are records appearing in the second list, however for whatever reason they're not getting matched in the RemoveAll()
Thanks
Ok only minutes after posting this I realised what I did wrong: The scoping is incorrect. What it should've been was like so:
public List<int> GetFilteredEmployeeIds(int? brandId)
{
List<int> employeeIds = GetFilteredEmployeeIdsBySearchTerm();
List<int> filteredEmployeeIds = employeeIds.Clone();
List<int> filteredEmployeeIdsByBrand = new List<int>();
// Now filter the results based on which checkboxes are ticked
foreach (var employeeId in employeeIds)
{
Member m = new Member(employeeId);
if (m.IsInGroup("Employees"))
{
int memberBrandId = Convert.ToInt32(m.getProperty("brandID").Value);
// Filter by brand
if (brandId != null)
{
if (brandId == memberBrandId)
filteredEmployeeIdsByBrand.Add(m.Id);
}
}
}
var setToRemove = new HashSet<int>(filteredEmployeeIdsByBrand);
filteredEmployeeIds.RemoveAll(x => !setToRemove.Contains(x));
return filteredEmployeeIds;
}
Essentially the removal of entries needed to be done outside the loop of the employee ids :-)
I know that you said your example was stripped down, so maybe this wouldn't suit, but could you do something like the following:
public List<int> GetFilteredEmployeeIds(int? brandId)
{
List<int> employeeIds = GetFilteredEmployeeIdsBySearchTerm();
return employeeIds.Where(e => MemberIsEmployeeWithBrand(e, brandId)).ToList();
}
private bool MemberIsEmployeeWithBrand(int employeeId, int? brandId)
{
Member m = new Member(employeeId);
if (!m.IsInGroup("Employees"))
{
return false;
}
int memberBrandId = Convert.ToInt32(m.getProperty("brandID").Value);
return brandId == memberBrandId;
}
I've just done that off the top of my head, not tested, but if all you need to do is filter the employee ids, then maybe you don't need to clone the original list, just use the Where function to do the filtering on it directly??
Please someone let me know if i've done something blindingly stupid!!
Still a beginner so please bare with them me on this. I would like the ability to add a number value to my property after the list distinguishes that there are duplicate values.
For example, two users have inputted into the system that they have the same employee ID of 001, the List allows them both to go in then applies a condition to check the list for any duplicates so in this case there would be. Now this conditional is true, add 1 to that property so the last employee now has employee ID of 002.
I believe LINQ will more than likely be involved in this process, but not to sure on how to go about this. I understand I will also have to update my List after performing this after to maintain the properties state throughout the program.
Thanks in advance hope everything is clear, question again How to add value to a property within a List<>
Code Snippet below.
Employee = new Employee(employeeFirstName, employeeLastName,001); // Hard coded for sake of example.
EmployeeList.AddEmployee(Employee);
EmployeeList Class
public static void AddEmployee(Employee employee)
{
employees.Add(new Employee(employee.FirstName,employee.LastName,employee.EmployeeID));
employees.Add(new Employee("John", "Jones", 001));
}
public void employeeIdValidation()
{
if (employees.Count() != employees.Select(x => new {staffId = x.EmployeeID }).Distinct().Count())
{
Console.WriteLine("Every Book and Category should be unique");
// employee ID increments by 1
// update List
}
else
{
Console.WriteLine("No duplicate found");
}
}
public static List<Employee> GetEmployeeList()
{
return employees; // With the updated EmployeeID
}
IMHO, you should use a Dictionary<int, Employee> (where the key is the Id) instead of a list. This will force you to update the ID of a duplicate record before adding to the collection and save a whole bunch of trouble and computation.
To find in your list what Employees have the same Id you can use this linq function.
var result=employees.ToLookup(e => e.Id);
At this way you can group the employees that have the same Id. Then, you can iterate for each group of employees with the same id as I show you below:
foreach (IGrouping<int, Employee> group in result)
{
//More than 2 employees with the same Id
if (group.Count()>1)
{
foreach (Employee employee in group)
{
//Change your ids here
}
}
}
[Update 1]:
To change the Ids I proporse you find first the max id before iterate in the groups.
int nextID = result.Max(e => e.Key)+1;
Then, change the second foreach cycle for a for cycle as you can see below. When you find a group with more than two Employees with the same id, don't change the first element, change the rest at this way:
//The first element don't change the id, start for the second element
for (int j = 1; j < group.Count(); j++)
{
var currentEmployee=group.ElementAt(j);
// Change the id and refresh the nextID variable
currentEmployee.Id = nextID++;
}
I have dictionary that is populated and I have no control of.
I need to modify the value how can I do that?
I have put a noddy example together to explain the problem
class Program
{
static void Main(string[] args)
{
Dictionary<Customer, int> CustomerOrderDictionary = new Dictionary<Customer, int>();
CustomerOrderDictionary.Add(new Customer { Id = 1, FullName = "Jo Bloogs" },3);
CustomerOrderDictionary.Add(new Customer { Id = 2, FullName = "Rob Smith" },5);
//now I decide to increase the quantity but cannot do the below as value has no setter
foreach (var pair in CustomerOrderDictionary)
{
if(pair.Key.Id==1)
{
pair.Value = 4;///ERROR HERE
}
}
}
}
public class Customer
{
public int Id { get; set; }
public string FullName { get; set; }
}
Any suggestions?
Thanks a lot
I suggest you work out which keys need modifying first, and then iterate over those modifications. Otherwise you'll end up modifying a collection while you're iterating over it, which will throw an exception. So for example:
// The ToList() call here is important, so that we evaluate all of the query
// *before* we start modifying the dictionary
var keysToModify = CustomerOrderDictionary.Keys
.Where(k => k.Id == 1)
.ToList();
foreach (var key in keysToModify)
{
CustomerOrderDictionary[key] = 4;
}
The problem here is that pair is typed to KeyValuePair which is a readonly object and can't be modified. Additionally the KeyValuePair collection is a way of viewing the contents of the dictionary (not changing it).
What you want to do here is just modify the dictionary directly. The Key in the KeyValuePair can be used to update the same entry in the dictionary.
if(pair.Key.Id==1) {
CustomerOrderDictionary[pair.Key] = 4;
}
EDIT
As Jon pointed out the assignment will invalidate the iterator. The simplest, but ineffecient route, is to copy the enumerator at the start of the loop.
foreach (var pair in CustomerOrderDictionary.ToList())
Here is an alternate approach
1) Create a new class
// wrapper class to allow me to edit a dictionary
public class IntWrapper
{
public int OrderCount{ get; set; }
}
2) Change this declaration
Dictionary<Customer, IntWrapper> CustomerOrderDictionary = new Dictionary<Customer, IntWrapper>();
3) Assign your variable
pair.Value.OrderCount = 4;
foreach (Customer customer in customers.Keys)
{
if ( customer.Id == 1 )
customers[ customer ] = 4;
}
CustomerOrderDictionary[1] = 4;
Here's one way to do that (just the assigning a value part..):
CustomerOrderDictionary[new Customer { Id = 1, FullName = "Jo Bloogs" }]=4
Notice that "1" is not a key in your dictionary. a Customer is, so you'll have to use that.
Notice also that Customer Should implement IEquatable as explained here
Ok, in your example you're effectively just finding the entry for the Customer object with Id = 1 and updating the associated value. In practice, I think that your code will likely be able to obtain a reference to your intended Customer object prior to updating the associated value in the dictionary. If that is the case, then there's no need for a loop.
Below is a very simple example where a loop is not needed because your code already has a reference to the customer1 variable. While my example is overly simplified, the concept is that you could potentially obtain a reference to your desired Customer object through some means other than iterating over the dictionary.
static void Main(string[] args)
{
Dictionary<Customer, int> CustomerOrderDictionary = new Dictionary<Customer, int>();
Customer customer1 = new Customer { Id = 1, FullName = "Jo Bloogs" };
Customer customer2 = new Customer { Id = 2, FullName = "Rob Smith" };
CustomerOrderDictionary.Add(customer1, 3);
CustomerOrderDictionary.Add(customer2, 5);
// you already have a reference to customer1, so just use the accessor on the dictionary to update the value
CustomerOrderDictionary[customer1]++;
}
If you need to perform some kind of update on multiple Customer objects based on some other criteria, then you might need a loop. The following example assumes that you'll have some collection other than the dictionary that stores your Customer objects, and that you can use that collection of Customer objects to identify the ones whose associated value in the dictionary need to be updated.
static void Main(string[] args)
{
// presumably you will have a separate collection of all your Customer objects somewhere
List<Customer> customers = new List<Customer>();
Customer customer1 = new Customer { Id = 1, FullName = "Jo Bloogs" };
Customer customer2 = new Customer { Id = 2, FullName = "Rob Smith" };
Customer customer3 = new Customer { Id = 3, FullName = "Rob Zombie" };
customers.Add(customer1);
customers.Add(customer2);
customers.Add(customer3);
Dictionary<Customer, int> CustomerOrderDictionary = new Dictionary<Customer, int>();
CustomerOrderDictionary.Add(customer1, 3);
CustomerOrderDictionary.Add(customer2, 5);
// let's just say that we're going to update the value for any customers whose name starts with "Rob"
// use the separate list of Customer objects for the iteration,
// because you would not be allowed to modify the dictionary if you iterate over the dictionary directly
foreach (var customer in customers.Where(c => c.FullName.StartsWith("Rob")))
{
// the dictionary may or may not contain an entry for every Customer in the list, so use TryGetValue
int value;
if (CustomerOrderDictionary.TryGetValue(customer, out value))
// if an entry is found for this customer, then increment the value of that entry by 1
CustomerOrderDictionary[customer] = value + 1;
else
// if there is no entry in the dictionary for this Customer, let's add one just for the heck of it
CustomerOrderDictionary.Add(customer, 1);
}
}
If this is not the case and the only source of Customer objects that you have available is the dictionary itself, then you'll need to perform some kind of cloning/copying of those objects out to a separate list/array prior to iterating over the dictionary for modification. See Jon Skeet's answer for this case; he suggests using a Where filter on the dictionary's Keys property and uses the ToList method to create a separate List<Customer> instance for the purpose of iteration.