1

My task is to add a number to an array if it is unique.

const [items, setItems] = useState(new Set());

const addNumber = () => {
  //add elements to an array if it is unique
  //is below line is right way to add element ? or i have to write like this - setItems(items.add(input)) ??
   items.add(input);
};
<button onClick={addNumber}>ADD Number</button>;
// trying to display the set data, throws an error:map is not a function
<ul>
{
items.map(item=>(<li>{item}</li>))
}

I want to iterate through set to display the data. Thanks in advance.

4
  • 1
    Have you looked into use Set developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Commented Jul 30, 2020 at 16:00
  • Hi, I have decided to go with Set. But struck in displaying the data. Please help me Commented Jul 31, 2020 at 5:13
  • @user3359964 : I have commented below why I think it is not a good idea to use Set, though, I would gladly support you to deploy my solution Commented Jul 31, 2020 at 6:37
  • @user3359964 : and, by the way, Set will work the best for primitive types, like strings or numbers but it won't help you to store more complex data (objects with multiple properties) which is usually the case Commented Jul 31, 2020 at 6:42

2 Answers 2

2

I wouldn't rely onto array length as it may change (items added and, what's worse, removed), so the simplest way would be to grab first unused id or maximum used id plus 1).

The latter would look as follows:

const addNumber = () => {
    const ids = items.map(({id}) => id),
          nextId = Math.max(...ids) + 1
    setItems([
          ...items, 
          {
            id: nextId,
            value: input
          }
    ])
}

The former, like this:

const addNumber = () => {
    const ids = items.map(({id}) => id),
          nextId = [...Array(x.length+1)]
            .map((_,i) => i)
            .find(n => !x.includes(n))
    setItems([
        ...items, 
        {
           id: nextId,
           value: input
        }
    ])
}

Following quick live-demo demonstrates the way of maintaining uniqueness of both record values and record id's:

const { useState } = React,
      { render } = ReactDOM,
      rootNode = document.getElementById('root')

const App = () => {
  const [items, setItems] = useState([]),
        [errorMsg, setErrorMsg] = useState(),
        onAddItem = e => {
            e.preventDefault()
            setErrorMsg(null)      
            const formData = new FormData(e.target),
                  value = formData.get('myInput'),
                  ids = items.map(({id}) => id),
                  nextId = [...Array(ids.length+1)]
            .map((_,i) => i)
            .find(n => !ids.includes(n))
            if(items.some(({value:v}) => v == value)){
              setErrorMsg('Value already exists')
            } else {
              setItems([
                ...items, 
                {
                  id: nextId,
                  value
                }
              ])
              e.target.reset()
            }
            
        },
        onDeleteItem = _id => 
          setItems(items.filter(({id}) => id !== _id))
  
  return (
    <div>
        <form onSubmit={onAddItem}>
          <input name="myInput" />
          <input type="submit" value="Add Item" />
          {errorMsg && <div className="errorMsg">{errorMsg}</div>}
        </form>
      {!!items.length && 
        (
          <ul>
            {
              items.map(({id, value}) => (
                <li key={id}>
                  {value}
                  <span 
                    onClick={() => onDeleteItem(id)}
                    className="removeButton"
                  >
                    ❌
                  </span>
                </li>
              ))
            }
          </ul>
        )
      }
    </div>
  )
}

render (
  <App />,
  rootNode
)
.removeButton {
  font-size: 10px;
  margin-left: 20px;
}

.removeButton:hover {
  cursor: pointer
}

.errorMsg {
  font-family: Arial;
  font-size: 10px;
  color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>

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

1 Comment

@user3359964 : I have appended live-demo to my answer to give you some clue on how to maintain both unique record values and unique record id's
2

There are a couple of ways to do this, first that comes to mind is, can you use a set rather than an array? Each element in a set is unique by default: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

The second is to conditionally add items to the array by first testing if they exist e.g.

const addNumber = (number) => {
  // findIndex will return -1 for items which don't already exist in the array
  const index = items.findIndex(item => item === number)

  if ( index === -1 ) {
    // make a copy of the index array to mutate
    const updatedItems = [ ...items ]
    updatedItems.push(number)
    setItems(updatedItems)
  }
}

1 Comment

While Set is a proper data structure to store unique values. This solution is not exactly applicable to React, as you'll need to store within your state duplicating data (unique id's). Furthermore, the bottleneck about .findIndex() which you outlined yourself may lead to situation, when first item will be pushed into state array with id: -1 which doesn't seem to be good thing to happen.

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.