Home > Software design >  How to insert a string into an array the same amount of times as the int value from an input?
How to insert a string into an array the same amount of times as the int value from an input?

Time:03-10

I have this mongoose Schema

const storeSchema = mongoose.Schema({
    name: {
        type: String,
        required: true,
    },
    fruits: {
        type: [String],
        required: true,
    },
});

On my client side I'm doing this for the name input, and it's working so far.

const [store, setStoreData] = useState({
    store: '',
    fruits: [''],
});

const dispatch = useDispatch();

const handleSubmit = (e) => {
    e.preventDefault();
    dispatch(addStore(store));
};

...


<TextField
    label='Store Name'
    variant='outlined'
    value={store.name}
    onChange={(e) =>
         setStoreData({
             ...store,
             name: e.target.value,
         })
    }
/>

I tried to imitate the behaviour but I think it has to be more complex. Problem is I wanted to have 2 or 3 of these "input select" for each fruit. And each would have multiple int choices like 1, 2, 3. And depending on the target value, push into the fruits array that number of fruit. This is what I had.

<Select
    value={store.fruits}
    label='Fruits'
    onChange={(e) =>
        setStoreData({
            ...store,
            fruits: e.target.value,
        })
    }>
    <MenuItem value={1}>1</MenuItem>
    <MenuItem value={2}>2</MenuItem>
    <MenuItem value={3}>3</MenuItem>
</Select>

So ideally I would have 3 of these inputs, one for each fruit like bananas and apples, and the number the client chose would be the number of entries that the number banana would be inserted into the array.

Example: A select that says I have 3 (the number 3) bananas. And I would get in the database this.

{
    "name": store
    "fruits": ["banana", "banana", "banana"] 
}

I am using @mui/material for react.

CodePudding user response:

A good solution might be to change the structure of your state to a more input friendly one, then transform this value to one that matches your store schema upon submission.

Your state declaration might look something like this;

const [store, setStoreData] = useState({
   name: "",
   fruits: {
      apple: 0,
      banana: 0,
      orange: 0
  },
});

You can then have a shared method to update the fruits value in your state.

const handleChangeFruit = (name, value) => {
        setStoreData(store => ({
            ...store,
            fruits: {
                ...store.fruits,
                [name]: value
            }
        }));
    }

Then your fruit Select input becomes:

<Select
    value={store.fruits.apple}
    label='apple'
    onChange={(e) =>
        handleChangeFruit('apple', parseInt(e.target.value))
    }>
    <MenuItem value={1}>1</MenuItem>
    <MenuItem value={2}>2</MenuItem>
    <MenuItem value={3}>3</MenuItem>
</Select>

To transform this state to your schema shape, you can do something like below;

const getStoreData = () => {
  const fruits = Object.entries(store.fruits).reduce(
    (acc, cur) => acc.concat(new Array(cur[1]).fill(cur[0])),
    []
  );
  return {
    name: store.name,
    fruits,
  };
};

Your handleSubmit method now becomes:

const handleSubmit = (e) => {
  e.preventDefault();
  dispatch(addStore(getStoreData()));
};

This should do the trick.

To get extra smart points, you can even generate the Select fields dynamically like so:

Object.keys(store.fruits).map((fruit) => (
  <Select
    key={fruit}
    value={store.fruits[fruit]}
    label={fruit}
    onChange={(e) => handleChangeFruit(fruit, parseInt(e.target.value))}
  >
    <MenuItem value={1}>1</MenuItem>
    <MenuItem value={2}>2</MenuItem>
    <MenuItem value={3}>3</MenuItem>
  </Select>
));
  • Related