2

I am basically trying to prevent duplicate objects from being created by iterating through the myLibrary array. If user input is a unique title, then add to array. If not, then deny entry.

No matter what I try, I get to the point where my code tells me a duplicate exists but still adds the duplicate object.

I am obviously new to this. Thank you in advance for any help.

let myLibrary = [];

class BookInfo {
    constructor(title, author, pages, genre) {
        this.title = title,
        this.author = author,
        this.pages = pages,
        this.genre = genre
    };
};

// Add new book to library array:
const addBook = function() {
    let title = document.getElementById("title");
    let author = document.getElementById("author");
    let pages = document.getElementById("pages");
    let genre = document.getElementById("genre");
    document.getElementById("Submit").addEventListener('click', (e) => {
        e.preventDefault();
        const newBook = new BookInfo(title.value, author.value, pages.value, genre.value);
        if (myLibrary.length === 0) {
            myLibrary.push(newBook); // Add first book to library.
            alert(`${title.value} has been added to your library!`);
            document.querySelector('form').reset(); // clear form after submit.
        } else {
            myLibrary.forEach(book => {
                console.log(newBook.title);
                if (book.title === newBook.title) { // Dup exists.
                    alert(`${title.value} already exists in your library.`);
                    document.querySelector('form').reset(); // clear form after submit.
                    return;
                } else {
                    myLibrary.push(newBook);
                    alert(`${title.value} has been added to your library.`);
                    document.querySelector('form').reset(); // clear form after submit.
                    return;
                }
            })
        };
    });
};

addBook();
    <form>
    <input type="text" id="title">
    <input type="text" id="author">
    <input type="number" id="pages">
    <input type="text" id="genre">
    <input type="button" value="Submit" id="Submit">
    </form>
Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
Niko17x
  • 21
  • 4
  • Now that I'm thinking about it, the myLibrary.forEach() will iterate through each object in the array. So this means that I can be getting both true and false causing both if and else statements to run. Hmm.. how to fix that. – Niko17x Jul 10 '22 at 19:58
  • i don't see a problem here, when `book.title === newBook.title` it does not push the object – MWO Jul 10 '22 at 20:15
  • Take a look at this question. [https://stackoverflow.com/questions/2641347/short-circuit-array-foreach-like-calling-break](https://stackoverflow.com/questions/2641347/short-circuit-array-foreach-like-calling-break) I think you should avoid use "forEach" if you need break the loop. (you are not using break but the use of the "return" will not work as expected) – Carlos Filipe Braz Jul 10 '22 at 20:49

3 Answers3

1

The built-in "forEach" method from Array.prototype wil not break with "break" or "return" https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach (look at the first blue note box)

In your case you can iterate using a for loop

for (let i = 0; i < myLibrary.length; i++) {
   if (myLibrary[i].title === book.title) {
     break;
   } else if (i == myLibrary.length - 1) {
     myLibrary.push(book);
   }
}

(instead of "myLibrary.forEach")

Or you can add always and clean the duplication after that:

myLibrary = myLibrary.filter(
  (i, idx) => myLibrary.findIndex((j) => i.title === j.title) === idx
);
1

You are looping the array and for each item, if it is not a match of the item to be inserted, then the item is inserted. The logical problem with this is that this will add the new item as many times as many mismatches were found in the array, independently of whether the item is already in the array. Instead of your approach, I'm using filter, which is a function that returns the subset of the array that matches the condition. In our case the condition is that it's matching. So, if there are any matches, then this subset will not be empty and therefore its length will differ from 0. So, I compare length to 0 and if it is indeed 0, it means it's a new item and it can be inserted. In that case, I'm pushing it to myLibrary, notify that it was added, reset the form and display the current content in a div. This displaying is unnecessary, but it was helpful in this example, so we can know whether this works properly

If the length is not 0, then I'm alerting that it's a duplicate and I do not do anything else, not even reset (because in the case of a typo it's not really user-friendly if the user would be punished by having to enter everything again.)

let myLibrary = [];

class BookInfo {
    constructor(title, author, pages, genre) {
        this.title = title,
        this.author = author,
        this.pages = pages,
        this.genre = genre
    };
};

// Add new book to library array:
const addBook = function() {
    let title = document.getElementById("title");
    let author = document.getElementById("author");
    let pages = document.getElementById("pages");
    let genre = document.getElementById("genre");
    document.getElementById("Submit").addEventListener('click', (e) => {
        e.preventDefault();
        if (myLibrary.filter(item => item.title === title.value).length === 0) {
            myLibrary.push(new BookInfo(title.value, author.value, pages.value, genre.value));
            alert("added");
            document.querySelector("form").reset();
            document.querySelector('div').innerText = JSON.stringify(myLibrary);
        } else {
            alert("duplicate");
        }
    });
};

addBook();
<form>
<input type="text" id="title" placeholder="title"><br>
<input type="text" id="author" placeholder="author"><br>
<input type="number" id="pages" placeholder="pages"><br>
<input type="text" id="genre" placeholder="genre"><br>
<input type="button" value="Submit" id="Submit">
</form>

<div></div>
Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
1

You cannot know in the first iteration whether the book title is new. For that you need to first complete all iterations.

But, why not use a different data structure for the library, one that is keyed by title. Then it doesn't need any iteration at all.

let myLibrary =  {};  // Different structure.

class BookInfo {
    constructor(title, author, pages, genre) {
        this.title = title;
        this.author = author;
        this.pages = pages;
        this.genre = genre;
    };
}

let title = document.getElementById("title");
let author = document.getElementById("author");
let pages = document.getElementById("pages");
let genre = document.getElementById("genre");
document.getElementById("Submit").addEventListener('click', (e) => {
    e.preventDefault();
    if (Object.hasOwn(myLibrary, title.value)) {
        alert("duplicate");
    } else {
        myLibrary[title.value] = new BookInfo(title.value, author.value, pages.value, genre.value);
        alert("added");
        document.querySelector("form").reset();
        document.querySelector('div').innerText = JSON.stringify(myLibrary);
    }
});
<form>
<input type="text" id="title" placeholder="title"><br>
<input type="text" id="author" placeholder="author"><br>
<input type="number" id="pages" placeholder="pages"><br>
<input type="text" id="genre" placeholder="genre"><br>
<input type="button" value="Submit" id="Submit">
</form>

<div></div>
trincot
  • 317,000
  • 35
  • 244
  • 286