0

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);
    }
}
7
  • Hi and Welcome to Stackoverflow! Do you have a code snippet? What do you want to do with the foreach - find a book, or remove all books from inventory? Commented Dec 14, 2018 at 12:31
  • You can use foreach, for or linq to get the related instance Commented Dec 14, 2018 at 12:31
  • 1
    What kind of data-structure (list, dictionary...) do you use to store the books? Commented Dec 14, 2018 at 12:34
  • I intended to mention that in my question and I should have. It is a list and with the method I am working on I am seeking to remove a book from the inventory Commented Dec 14, 2018 at 12:35
  • @LP Edit your question and add that into it. Makes it clearer for everyone to help you Commented Dec 14, 2018 at 12:40

5 Answers 5

3

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

Sign up to request clarification or add additional context in comments.

1 Comment

If there are no duplicates, add a break statement inside the if block. If they are, do i--; otherwise you are skipping one item.
3

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);

Comments

0

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.

Comments

0

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]);
    }
}

Comments

0

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);
} 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.