使用Google Apps脚本将人员分组的最佳方法

时间:2019-09-13 22:50:18

标签: javascript google-apps-script google-sheets

这个部门基本上大约有200个。我们正在尝试创建一个4人小队,其中每个小队必须来自同一城市的不同团队,一个小队必须是车手(其他3个不应该是车手),并且车队中的所有四人都应该有相似的偏好。这些条件是通过Google表单捕获的,因此最终我们将获得建立条件所需的相关数据。目前我唯一的挑战是我对Apps Script并不精通,因此需要如何使用它的高级指导。

当前,我正在考虑将每一列存储在各自的数组中,然后在其中使用For循环和条件语句来尝试满足所有要求。例如,第一个条件语句将是人员是否开车,如果有人开车,则将其放入这个新数组(将要定义)。接下来,检查下一个响应,并且该人是否不开车,具有相同的首选项,位于同一城市,并且在不同的团队中,然后将其包括在该数组中。否则,请转到下一个人并检查这些要求。该小组打4人后,将值设置到Google表格中的某个位置,然后在列表中搜索下一个驱动程序,并使用相同的逻辑建立另一个小组。

另一种方法可能是先将人们分为两个组(基于两个城市),然后在每个组中根据驾驶员对人员进行分类,并添加其他条件。

无论我采用哪种算法,首先将每列存储到数组中都是最有意义的,然后使用各种条件语句来管理数组值。

这是我到目前为止所拥有的:

function matchUsers() {
  //store each column into arrays for use later on
  var responseSheet = SpreadsheetApp.getActive().getSheetByName("Responses");
  var Alast = responseSheet.getRange("A2:A").getValues().filter(String).length;
  Logger.log(Alast);
  var emails = responseSheet.getRange("A2:A").getValues();
  var userEmails = responseSheet.getRange(2,1, Alast,1).getValues();
  var tmName = responseSheet.getRange(2,2, Alast,1).getValues();
  var team = responseSheet.getRange(2,3, Alast,1).getValues();
  var city = responseSheet.getRange(2,4, Alast,1).getValues();
  var firstChoice = responseSheet.getRange(2,5, Alast,1).getValues();
  var secChoice = responseSheet.getRange(2,6, Alast,1).getValues();
  var thirdChoice = responseSheet.getRange(2,7, Alast,1).getValues();
  var driveStatus = responseSheet.getRange(2,8, Alast,1).getValues();
  var arrayResponses = [userEmails,tmName,team,city,firstChoice,secChoice,thirdChoice, driveStatus];

  //clear sheets
  SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Output").getRange(2, 1,20,50).clearContent();
  SpreadsheetApp.getActiveSpreadsheet().getSheetByName("CitySort").getRange(2, 1,20,50).clearContent();

  //prepare arrays
  var edmTMs = [];
  var calTMs = [];
  var edmDrivers = [];
  var calDrivers = [];

  //loop through each row and sort team members by respective city and driving capability
  for (let i = 0; i < Alast;i+=1){
    if (arrayResponses[3][i] == "Edmonton" && arrayResponses[7][i] == "Yes"){
      edmDrivers.push(arrayResponses[1][i]);
    }else if (arrayResponses[3][i] == "Edmonton" && arrayResponses[7][i] == "No"){
      edmTMs.push(arrayResponses[1][i]);
    }else if (arrayResponses[3][i] == "Calgary" && arrayResponses[7][i] == "Yes"){
      calDrivers.push(arrayResponses[1][i]);
    }else if (arrayResponses[3][i] == "Calgary" && arrayResponses[7][i] == "No"){
      calTMs.push(arrayResponses[1][i]);
    }
  }

  Logger.log("\nEdmonton: "+edmTMs + "\nCalgary: "+calTMs );
  var range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("CitySort").getRange(2,1,edmTMs.length,1);
  range.setValues(edmTMs);
  var range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("CitySort").getRange(2,2,calTMs.length,1);
  range.setValues(calTMs);
  var range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("CitySort").getRange(2,3,edmDrivers.length,1);
  range.setValues(edmDrivers);
  var range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("CitySort").getRange(2,4,calDrivers.length,1);
  range.setValues(calDrivers);
}

1 个答案:

答案 0 :(得分:0)

让我们看看这是否符合您的需求。我敢肯定,有比这更优雅,更有效的解决方案了,因此,我很感谢任何建议。

我用34个用户测试了脚本,得到了8个4人小组和2个1人小组。

enter image description here

不要介意重复的名称,它们都是具有不同城市,团队和选择的不同行。我给其中一些人加了一封信,以区别对待。

脚本的工作方式如下:

它获取每个用户行,并将“城市”,“团队”和“选择”与所有其他33行进行比较。它还会丢弃已经在团队中的用户。优先级是城市>团队>选择1>选择2>选择3。

它还将尝试迫使4名成员组成的团队,因此,如果有3名成员组成的团队,它将忽略该团队以寻求第4名,但仍会尊重选择和城市。您可以通过将布尔值ignoreTeams设置为false来禁用此功能。在我的测试中,只有3个小组有1个重复小组。

该脚本永远不会将来自不同城市的两个用户放在同一个组中。

function main() {

  var sprsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet_1 = sprsheet.getSheetByName("Members");
  var sheet_2 = sprsheet.getSheetByName("Output");

  var number = sheet_1.getLastRow() - 1;
  var members = []; //Each row of Members will be stored here

  getMembers(sheet_1, members, number);
  orderMembers(sheet_2, members);

}

function getMembers(sheet_1, members, number){
  for (var i = 0; i < number; i++){
    var values = sheet_1.getRange(i+2, 1, 1, 8).getValues();
    members.push(new Person(values[0][0],values[0][1],values[0][2],values[0][3],values[0][4],values[0][5],values[0][6],values[0][7]));

  }

}

function orderMembers(sheet_2, members){
  var col = 1;
  var counter = 0; //Number of Persons in each group column [0-3] 

  loop1:
  for (var i = 0; i < members.length; i++){
    var loopbreaker = 0; //Avoids an endless loop if ignoreTeams = true
    var ignoreTeams = false; 
    var row = 2;
    if (!members[i].assigned || i == 0){
      var teams = []; //Array of teams of each group
      sheet_2.getRange(row, col).setValue(members[i].name);
      members[i].assigned = true;
      teams.push(members[i].team);

      loop2:
      for (var loop = 0; loop < 9; loop++){
        var value = order_params(i, loop, members, teams, ignoreTeams);

        if (value != null){
          sheet_2.getRange(row + 1, col).setValue(value); //Writes in sheet Output     
          counter++;
          row++;

        }
        //If the group is smaller than 4 and the loop is over, it will ignore the teams and start again in order to complete the group.
        if (counter < 3 && loop == 8 && loopbreaker < 1){
          loop = 0; 
          ignoreTeams = true; //Set to false to never ignore teams
          loopbreaker++;//The loopbreaker avoids an endless loop
        } else if (counter == 3 || loop == 8){
          col++;
          counter = 0;
          break loop2;
        }

      }
    }
  }


}

function order_params(i, loop, members, teams, ignoreTeams){
  for (var x = 0; x < members.length; x++){

    if (!members[x].assigned && members[x].email != members[i].email && members[x].city == members[i].city  && (!checkTeams(members[x].team, teams) || ignoreTeams)) {
      if (members[x].choice1 == members[i].choice1 && loop < 3) {

        members[x].assigned = true;
        teams.push(members[x].team);
        return members[x].name
      } else if (members[x].choice2 == members[i].choice1 && loop >= 3 && loop < 6) {

        members[x].assigned = true;
        teams.push(members[x].team);
        return members[x].name

      } else if (members[x].choice3 == members[i].choice1 && loop >= 6 && loop < 9) {

        members[x].assigned = true;
        teams.push(members[x].team);
        return members[x].name

      } 
    }
  }

}
//Checks for duplicate teams in the group
function checkTeams(member_t, teams){
  for (var i = 0; i < teams.length; i++){
    if (member_t == teams[i]){

      return true; 
    }
  }
}
//Converts the data from the Sheet into Person objects.
function Person(email, name, team, city, choice1, choice2, choice3, driver){
  this.email = email;
  this.name = name;
  this.team = team;
  this.city = city;
  this.choice1 = choice1;
  this.choice2 = choice2;
  this.choice3 = choice3;
  this.driver = driver;
  this.assigned = false; //Assigned = true means the Person is already in a group

}

如您所见,我没有使用“驱动程序”字段,因此可以设置条件,以防看到太多没有驱动程序的组,或者手动更改成员。如果这是一个严重的问题,我将修改代码。