1
votes

I'd like to achieve the following scenario, I have a game and the setup process needs a line up connected to 2 databases, based on availability.

1. players
2. Vehicles

The lineup includes a "builder" meaning each cell has a drop down list connected to the database and shows available objects.

My goal

I need to auto-update a dropdown list as soon as I submit or select one object from the list, so the next cell won't have that object any more once I click the dropdown, also if I delete an object from the lineup this object has to go back to the dropdown list since its available again.

What have I done?

I did some formulas on the database side so the dropdown list can be created from a column that auto-updates once I assign an object to the list from the dropdown itself.

column 1: includes all available vehicles/players
column 2: includes all assigned vehicles/players
column 3: generates the dropdown list subtracting column1 from column2 using a formula =SORT(FILTER(E2:E,F2:F=""),1,true)

The Issue

while this method "works" I have some issues with the way the dropdown auto-updates If I add all available equipment and then delete and item so it could be available for another cell/slot the dropdown list won't auto-update correctly, if I delete "unit 4" the dropdown list will show me another object available "unit 8".

What can I do to solve my problem?

I am adding the link for the spreadsheet so is easier to understand my issue.

Thank you guys.

googlespreadsheet

2
Please choose one approach, where they have some similarities, the differences will make many answers unusable across platforms.Scott Craner

2 Answers

0
votes

So this solution could be optimized and cleaned out adding some functions to not repeat the code. But as a working starting point you could have.

function onEdit(e) {
  var ss = SpreadsheetApp.getActive();

  // Get the working Sheets
  var gameplan = ss.getSheetByName("gameplan");
  var vehicle = ss.getSheetByName("vehicle");
  var players = ss.getSheetByName("players");

  // Get current selected vehicles
  var _selectedVehicles = gameplan.getRange(2, 2, gameplan.getLastRow() - 1).getValues();
  var selectedVehicles = [];
  for(var i = 0; i < _selectedVehicles.length; i++){
    selectedVehicles.push(_selectedVehicles[i][0])
  }

  // Get current selected players
  var _selectedPlayers = gameplan.getRange(2, 1, gameplan.getLastRow() - 1).getValues();
  var selectedPlayers = [];
  for(var i = 0; i < _selectedPlayers.length; i++){
    selectedPlayers.push(_selectedPlayers[i][0])
  } 





  // Get active and unselected Vehicles
  var activeVehicles = [];
  var vehicleValues = vehicle.getRange(2, 1, vehicle.getLastRow() - 1, 2).getValues();

  for(var i =0; i < vehicleValues.length; i++){
    if(vehicleValues[i][0] == "ACTIVE" && selectedVehicles.indexOf(vehicleValues[i][1]) == -1){
      activeVehicles.push(vehicleValues[i][1])
    }
  }

  // Get active and unselected players
  var activePlayers = [];
  var playerValues = players.getRange(2, 1, players.getLastRow() - 1, 2).getValues();

  for(var i =0; i < playerValues.length; i++){
    if(playerValues[i][0] == "ACTIVE" && selectedPlayers.indexOf(playerValues[i][1]) == -1){
      activePlayers.push(playerValues[i][1])
    }
  }  



  // Insert data validation for vehicles
  var rule = SpreadsheetApp.newDataValidation().requireValueInList(activeVehicles, true).build();
  gameplan.getRange("B2:B").setDataValidation(rule);

  // Insert data validation for Players
  var rule = SpreadsheetApp.newDataValidation().requireValueInList(activePlayers, true).build();
  gameplan.getRange("A2:A").setDataValidation(rule);



}

Explanation

Is not clear from your post if you have used Apps Script before. So just to make sure you can understand what is happening (in case you want to modify it) I will go over my code explaining all the steps and methods I have used.

Basically the code has 4 parts.

  • Getting the working sheets
// Get the working Sheets
  var gameplan = ss.getSheetByName("gameplan");
  var vehicle = ss.getSheetByName("vehicle");
  var players = ss.getSheetByName("players");

Here is just basically invoking the getSheetByName() on the spreadsheet.

  • Get the already selected values
 // Get current selected vehicles
  var _selectedVehicles = gameplan.getRange(2, 2, gameplan.getLastRow() - 1).getValues();
  var selectedVehicles = [];
  for(var i = 0; i < _selectedVehicles.length; i++){
    selectedVehicles.push(_selectedVehicles[i][0])
  }

  // Get current selected players
  var _selectedPlayers = gameplan.getRange(2, 1, gameplan.getLastRow() - 1).getValues();
  var selectedPlayers = [];
  for(var i = 0; i < _selectedPlayers.length; i++){
    selectedPlayers.push(_selectedPlayers[i][0])
  } 

So here is just repeating the same code two times. Inside every sheet calling the getRange method. Then because the return type is Object[][] we need to iterate through and get every individual value and push it to the selectedVehicles (or players) array.

  • Get the active and unselected Vehicles
 // Get active and unselected Vehicles
  var activeVehicles = [];
  var vehicleValues = vehicle.getRange(2, 1, vehicle.getLastRow() - 1, 2).getValues();

  for(var i =0; i < vehicleValues.length; i++){
    if(vehicleValues[i][0] == "ACTIVE" && selectedVehicles.indexOf(vehicleValues[i][1]) == -1){
      activeVehicles.push(vehicleValues[i][1])
    }
  }

So in here is the same getRange method to get the values, but in this case we are retrieving the two columns, the ACTIVE/INACTIVE and the id. After that we iterate through the array making sure that that row has the ACTIVE status and that the id is not in the previous selected array. Look at the indexOf. And we store all the values that match those condtitions in a new array.

  • Finally we impose the new data validation
// Insert data validation for vehicles
  var rule = SpreadsheetApp.newDataValidation().requireValueInList(activeVehicles, true).build();
  gameplan.getRange("B2:B").setDataValidation(rule);

For this last bit you need to use is the newDataValidation() method that creates a DataValidationBuilder objects. In that object you have a a lot of methods to make data behave how you want, one being requireValueInList to have a dropdown with your desired values. In the case the array that we have created in the previous step. After using build we can add this new rule to a range with sedDataValidation.

0
votes

I have a way to resolve your issue, but it requires a "helper" column/row for each selection and also a "checker" column.

The checker would check if the item is used, and the helper column for each would be based on the available selections left.

Using your column 1 and column 2, you would just need an additional helper column for each selection.

Below is an example:
A                   B   B   D   E                   Column 1 (K)   Column 2(L)
Chosen number 1     3   3   2   4                   Random items    
Chosen number 2     5   5   2   4                       1               3
Chosen number 3     1   1   2   4                       2            available
Chosen number 4         2   4                           3               1
Chosen number 5         2   4                           4            available
                                                        5               2


The formula in column C is '=B'
The formula in column D is '=transpose(filter($K$2:$K$6,$L$2:$L$6="available"))'
The data validation in column B is range C:F.
Column 1 includes all options
Column 2 includes all available options and the formula in column 2 is '=iferror(match(K2,$B$1:$B$5,0),"available")'

The purpose of column C is to allow for your data validation to be valid, even after entering in the selection.