Updating array of objects in mongoose based off key in objects value

Viewed 250

I have two ejs forms that when hit, make an HTTP post request to my /api/users/makePicks/:id route. This route hits my controller which updates the Users model in my mongodb with the NFL picks they submitted in the EJS form.

I need this route to create the picks object for each route if they do not exist for that particular week, and if they do exist it needs to update the picks that are already there. The picks are being stored in my User model in an array, this array contains objects for each weeks picks. Currently the code, with much help from Mohammed, is successfully pushing code to to array. But i cannot seem to figure out how to update the picks if an object with a key of that week exists.

My validation is finally working properly. What I mean is we are running a for loop on the picks array, it will console.log true if there is already a matching picks object with for that weeks picks, if the object with a first key value with the current weeks form doesn't exist, it will console.log false and push the new picks to the array.

The only part that isn't working is the if statement nested within my for loop, it is not updating the object if it already exists in the picks.array. But as I said, the validation is working correctly. I suspect the line of code

result.picks[i] = { [`week-${req.params.week}`]:req.body };

is for some reason not updating object with the updated req.body.

Controller

exports.makePicks = async (req, res, next) => {
  const picks = req.body;
  try {
   
    let result = await User.findById(req.user._id);
    
    if (result.picks.length > 0) {
      for (let i = 0; i < result.picks.length; i++) {
        if ((Object.keys(result.picks[i])[0] == [`week-${req.params.week}`])) {
          console.log(chalk.green("true"));
          result.picks[i] = { [`week-${req.params.week}`]:req.body };
          break;
        } else {
          console.log(chalk.red("false"));
          result.picks.push({ [`week-${req.params.week}`]: picks });
          break;
        }
      }
    } else {
      result.picks.push({ [`week-${req.params.week}`]: picks });
      console.log(chalk.yellow('results.picks is empty'))
    }

    
    await result.save();
    res.redirect("/api/dashboard");

  } catch (error) {
    console.log(error);
  }
};

result.picks example structure

[{"week-1":
    {"jojo-0":"ARI","jojo-1":"ARI","jojo-2":"ARI"}
},
{"week-2":
    {"jojo-0":"ATL","jojo-1":"ATL","jojo-2":"BAL"}
},
{"week-3":
    {"jojo-0":"ARI","jojo-1":"ARI","jojo-2":"ARI"}
}]

Router

router.route('/makePicks/:week')
  .post(controller.makePicks);

EJS

<% const teamsArr = ['ARI', 'ATL', 'BAL', 'BUF', 'CAR', 'CHI', 'CIN', 'CLE', 'DAL', 'DEN', 'DET', 'GB', 'HOU', 'IND', 'JAX',
'KC', 'LAC', 'LAR', 'LV', 'MIA', 'MIN', 'NE', 'NO', 'NYG','NYJ', 'PHI', 'PIT', 'SEA', 'SF', 'TB', 'TEN', 'WAS' ] %>

<form class="mt-3 mb-3" method="POST" action="/api/users/makePicks/1">
  <% for(i=0; i < user.bullets; i++){ %>
  <div class="form-group">
    <label for="<%= `${user.name}-${i}` %>">Make your pick for bullet <%= `${i}` %></label>
    <select class="form-control" name="<%= `${user.name}-${i}` %>" id="<%= `${user.name}-${i}` %>">
      <% teamsArr.forEach(team => { %>
        <option value="<%= team %>"><%= team %></option>
      <% }) %>
    </select>
  </div>
  <% }; %>
  <button type="submit" class="btn btn-primary">Save changes</button>
</form>

<form class="mt-3 mb-3" method="POST" action="/api/users/makePicks/2">
  <% for(i=0; i < user.bullets; i++){ %>
  <div class="form-group">
    <label for="<%= `${user.name}-${i}` %>">Make your pick for bullet <%= `${i}` %></label>
    <select class="form-control" name="<%= `${user.name}-${i}` %>" id="<%= `${user.name}-${i}` %>">
      <% teamsArr.forEach(team => { %>
        <option value="<%= team %>"><%= team %></option>
      <% }) %>
    </select>
  </div>
  <% }; %>
  <button type="submit" class="btn btn-primary">Save changes</button>
</form>
2 Answers

you want to using $push and $set in one findByIdAndUpdate, that's impossible, I prefer use findById() and process and save() so just try

exports.makePicks = async (req, res, next) => {
  const picks = req.body;
  try {
    //implementation business logic
    let result = await User.findById(req.user._id)
    if(result.picks && result.picks.length > 0){
      result.picks.forEach(item =>{
        if([`week-${req.params.week}`] in item){
          item[`week-${req.params.week}`] = picks
        }
        else{
          result.picks.push({ [`week-${req.params.week}`] : picks })
        }
      })
    }
    else{
      result.picks.push({ [`week-${req.params.week}`] : picks })
    }

    await result.save()
    res.redirect('/api/dashboard');
  } catch (error) {
    console.log(error)
  }
}

note: don't use callback and async/await together

exports.makePicks = async (req, res, next) => {
  const picks = req.body;
  const { week } = req.params;
  try {
   
    let result = await User.findById(req.user._id);
    const data = { [`week-${week}`]: picks };
    const allPicks = [...result.picks];
    
    if (allPicks.length > 0) {

      // Search index of peek
      const pickIndex = _.findIndex(allPicks, (pick) => {
        return Object.keys(pick)[0] == `week-${week}`;
      });

      // If found, update it
      if (pickIndex !== -1) {
        console.log(chalk.green("true"));
        allPicks[pickIndex] = data;
      }

      // Otherwise, push new pick
      else {
        console.log(chalk.red("false"));
        allPicks.push(data);
      }

    } else {
      allPicks.push(data);
      console.log(chalk.yellow('results.picks is empty'))
    }

    result.picks = allPicks;
    console.log('allPicks', allPicks);
    
    await result.save();
    res.redirect("/api/dashboard");
  } catch (error) {
    console.log(error);
  }
};
Related