您如何在 Google 表格中进行动态/依赖下拉菜单?

Posted

技术标签:

【中文标题】您如何在 Google 表格中进行动态/依赖下拉菜单?【英文标题】:How do you do dynamic / dependent drop downs in Google Sheets? 【发布时间】:2014-03-11 18:13:18 【问题描述】:

如何根据谷歌表格中主类别下拉列表中选择的值获取子类别列以填充下拉列表?

我四处搜索,找不到任何好的解决方案,因此我想分享我自己的。请看下面我的回答。

【问题讨论】:

【参考方案1】:

您可以从设置主页和下拉源页面的谷歌表格开始,如下所示。

您可以通过正常的 Data > Validations 菜单提示设置第一列下拉菜单。

主页

下拉源页面

之后,您需要设置一个名称onEdit的脚本。 (如果您不使用该名称,getActiveRange() 只会返回单元格 A1)

并使用此处提供的代码:

function onEdit() 
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = SpreadsheetApp.getActiveSheet();
  var myRange = SpreadsheetApp.getActiveRange();
  var dvSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Categories");
  var option = new Array();
  var startCol = 0;

  if(sheet.getName() == "Front Page" && myRange.getColumn() == 1 && myRange.getRow() > 1)
    if(myRange.getValue() == "Category 1")
      startCol = 1;
     else if(myRange.getValue() == "Category 2")
      startCol = 2;
     else if(myRange.getValue() == "Category 3")
      startCol = 3;
     else if(myRange.getValue() == "Category 4")
      startCol = 4;
     else 
      startCol = 10
    

  if(startCol > 0 && startCol < 10)
    option = dvSheet.getSheetValues(3,startCol,10,1);
    var dv = SpreadsheetApp.newDataValidation();
    dv.setAllowInvalid(false);  
    //dv.setHelpText("Some help text here");
    dv.requireValueInList(option, true);
    sheet.getRange(myRange.getRow(),myRange.getColumn() + 1).setDataValidation(dv.build());
   

  if(startCol == 10)
    sheet.getRange(myRange.getRow(),myRange.getColumn() + 1).clearDataValidations();
   
  

之后,通过转到“编辑”>“当前项目触发器”在脚本编辑器屏幕中设置触发器。这将打开一个窗口,让您选择各种下拉菜单,最终以以下方式结束:

你应该好好去追求!

【讨论】:

感谢您发布此 tarheel,但是否缺少步骤?我如何真正获得填充第二个下拉列表的功能? @appski 我不知道。您确定function onEdit() 会在您对工作表中的单元格进行编辑(例如选择类别)时启动。 啊,我知道我做错了什么 - 我没有仔细查看您的代码,没有意识到我需要将标签重命名为“首页”和“类别”。现在我已经完成了,从 Category 下拉列表中进行选择会导致 SubCategory 下拉列表出现在第二列中。非常感谢你发布这个! 这段代码可以短很多。观看此视频,该视频展示了一种使用命名范围和 setDataValidation 的方法。我转录了代码检查视频下的cmets。 youtube.com/watch?v=ZgQPZMCBRuc 解决方案很笨拙,因为您必须为每个子菜单单独保留一个单独的列表。对于大型列表,这很快就会变得笨拙。管理变革也变得很麻烦。【参考方案2】:

编辑:下面的答案可能令人满意,但它有一些缺点:

    脚本运行有明显的暂停。我有 160 毫秒的延迟,这已经够烦人了。

    每次编辑给定行时,它都会构建一个新范围。这有时

    会为以前的条目提供“无效内容”

我希望其他人可以稍微清理一下。

这是另一种方法,可以为您节省大量范围命名:

工作表中的三个工作表:将它们称为 Main、List 和 DRange(用于动态范围。) 在主工作表上,第 1 列包含一个时间戳。此时间戳在编辑时被修改。

在列表中,您的类别和子类别被排列为一个简单的列表。我在我的林场里用它来做植物库存,所以我的清单是这样的:

Group   | Genus | Bot_Name
Conifer | Abies | Abies balsamea
Conifer | Abies | Abies concolor
Conifer | Abies | Abies lasiocarpa var bifolia
Conifer | Pinus | Pinus ponderosa
Conifer | Pinus | Pinus sylvestris
Conifer | Pinus | Pinus banksiana
Conifer | Pinus | Pinus cembra
Conifer | Picea | Picea pungens
Conifer | Picea | Picea glauca
Deciduous | Acer | Acer ginnala
Deciduous | Acer | Acer negundo
Deciduous | Salix | Salix discolor
Deciduous | Salix | Salix fragilis
...

在哪里 |表示分列。 为方便起见,我还使用标题作为命名范围的名称。

DRrange A1 有公式

=Max(Main!A2:A1000)

这将返回最近的时间戳。

A2 到 A4 有以下变化:

=vlookup($A$1,Inventory!$A$1:$E$1000,2,False) 

右边的每个单元格都会增加 2。

在运行 A2 到 A4 时将具有当前选择的组、属和种。

在每一个的下面,是一个类似这样的过滤命令:

=unique(filter(Bot_Name,REGEXMATCH(Bot_Name,C1)))

这些过滤器将在下面的块中填充与顶部单元格内容匹配的条目。

可以根据您的需要和列表格式修改过滤器。

返回 Main:Main 中的数据验证使用 DRange 中的范围完成。

我使用的脚本:

function onEdit(event) 

  //SETTINGS
  var dynamicSheet='DRange'; //sheet where the dynamic range lives
  var tsheet = 'Main'; //the sheet you are monitoring for edits
  var lcol = 2; //left-most column number you are monitoring; A=1, B=2 etc
  var rcol = 5; //right-most column number you are monitoring
  var tcol = 1; //column number in which you wish to populate the timestamp
  //

  var s = event.source.getActiveSheet();
  var sname = s.getName();
  if (sname == tsheet) 
    var r = event.source.getActiveRange();
    var scol = r.getColumn();  //scol is the column number of the edited cell
    if (scol >= lcol && scol <= rcol) 
      s.getRange(r.getRow(), tcol).setValue(new Date());
      for(var looper=scol+1; looper<=rcol; looper++) 
         s.getRange(r.getRow(),looper).setValue(""); //After edit clear the entries to the right
      
    
  

原始 Youtube 演示文稿为我提供了大部分 onEdit 时间戳组件: https://www.youtube.com/watch?v=RDK8rjdE85Y

【讨论】:

【参考方案3】:

这里有另一个基于@tarheel 提供的解决方案

function onEdit() 
    var sheetWithNestedSelectsName = "Sitemap";
    var columnWithNestedSelectsRoot = 1;
    var sheetWithOptionPossibleValuesSuffix = "TabSections";

    var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
    var activeSheet = SpreadsheetApp.getActiveSheet();

    // If we're not in the sheet with nested selects, exit!
    if ( activeSheet.getName() != sheetWithNestedSelectsName ) 
        return;
    

    var activeCell = SpreadsheetApp.getActiveRange();

    // If we're not in the root column or a content row, exit!
    if ( activeCell.getColumn() != columnWithNestedSelectsRoot || activeCell.getRow() < 2 ) 
        return;
    

    var sheetWithActiveOptionPossibleValues = activeSpreadsheet.getSheetByName( activeCell.getValue() + sheetWithOptionPossibleValuesSuffix );

    // Get all possible values
    var activeOptionPossibleValues = sheetWithActiveOptionPossibleValues.getSheetValues( 1, 1, -1, 1 );

    var possibleValuesValidation = SpreadsheetApp.newDataValidation();
    possibleValuesValidation.setAllowInvalid( false );
    possibleValuesValidation.requireValueInList( activeOptionPossibleValues, true );

    activeSheet.getRange( activeCell.getRow(), activeCell.getColumn() + 1 ).setDataValidation( possibleValuesValidation.build() );

它比其他方法有一些好处:

您无需在每次添加“根选项”时都编辑脚本。您只需使用此根选项的嵌套选项创建一个新工作表。 我重构了脚本,为变量等提供了更多语义名称。此外,我已经将一些参数提取到变量中,以便更容易适应您的具体情况。您只需设置前 3 个值。 嵌套选项值没有限制(我使用了带有 -1 值的 getSheetValues 方法)。

那么,如何使用它:

    创建包含嵌套选择器的工作表 转到“工具”>“脚本编辑器...”并选择“空白项目”选项 粘贴此答案所附的代码 修改脚本的前 3 个变量以设置您的值并保存 在同一文档中为“根选择器”的每个可能值创建一个工作表。它们必须命名为值 + 指定的后缀。

享受吧!

【讨论】:

【参考方案4】:

继续改进此解决方案,我通过添加对多个根选择和更深的嵌套选择的支持来提高赌注。这是 JavierCane 解决方案的进一步发展(它又建立在 tarheel 的基础上)。

/**
 * "on edit" event handler
 *
 * Based on JavierCane's answer in 
 * 
 *   http://***.com/questions/21744547/how-do-you-do-dynamic-dependent-drop-downs-in-google-sheets
 *
 * Each set of options has it own sheet named after the option. The 
 * values in this sheet are used to populate the drop-down.
 *
 * The top row is assumed to be a header.
 *
 * The sub-category column is assumed to be the next column to the right.
 *
 * If there are no sub-categories the next column along is cleared in 
 * case the previous selection did have options.
 */

function onEdit() 

  var NESTED_SELECTS_SHEET_NAME = "Sitemap"
  var NESTED_SELECTS_ROOT_COLUMN = 1
  var SUB_CATEGORY_COLUMN = NESTED_SELECTS_ROOT_COLUMN + 1
  var NUMBER_OF_ROOT_OPTION_CELLS = 3
  var OPTION_POSSIBLE_VALUES_SHEET_SUFFIX = ""
  
  var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet()
  var activeSheet = SpreadsheetApp.getActiveSheet()
  
  if (activeSheet.getName() !== NESTED_SELECTS_SHEET_NAME) 
  
    // Not in the sheet with nested selects, exit!
    return
  
  
  var activeCell = SpreadsheetApp.getActiveRange()
  
  // Top row is the header
  if (activeCell.getColumn() > SUB_CATEGORY_COLUMN || 
      activeCell.getRow() === 1 ||
      activeCell.getRow() > NUMBER_OF_ROOT_OPTION_CELLS + 1) 

    // Out of selection range, exit!
    return
  
  
  var sheetWithActiveOptionPossibleValues = activeSpreadsheet
    .getSheetByName(activeCell.getValue() + OPTION_POSSIBLE_VALUES_SHEET_SUFFIX)
  
  if (sheetWithActiveOptionPossibleValues === null) 
  
    // There are no further options for this value, so clear out any old
    // values
    activeSheet
      .getRange(activeCell.getRow(), activeCell.getColumn() + 1)
      .clearDataValidations()
      .clearContent()
      
    return
  
  
  // Get all possible values
  var activeOptionPossibleValues = sheetWithActiveOptionPossibleValues
    .getSheetValues(1, 1, -1, 1)
  
  var possibleValuesValidation = SpreadsheetApp.newDataValidation()
  possibleValuesValidation.setAllowInvalid(false)
  possibleValuesValidation.requireValueInList(activeOptionPossibleValues, true)
  
  activeSheet
    .getRange(activeCell.getRow(), activeCell.getColumn() + 1)
    .setDataValidation(possibleValuesValidation.build())
    
 // onEdit()

正如哈维尔所说:

创建包含嵌套选择器的工作表 转到“工具”>“脚本编辑器...”并选择“空白项目” 选项 粘贴此答案所附的代码 修改脚本顶部的常量以设置您的值 并保存 在同一文档中为每个可能的值创建一个工作表 “根选择器”。它们必须命名为值 + 指定的 后缀。

如果你想看到它的实际效果,我创建了a demo sheet,如果你复制,你可以看到代码。

【讨论】:

【参考方案5】:

注意!脚本有一个限制:它在一个下拉列表中最多可以处理 500 个值。

Google 表格中的多行、多级、多列表、多编辑行相关下拉列表。脚本

更多信息

Article Video script on GitHub 的最新版本

这个解决方案并不完美,但它带来了一些好处:

    让你制作多个下拉列表 提供更多控制权 源数据放置在唯一的工作表上,因此编辑起来很简单

首先,这里是working example,所以你可以在继续之前对其进行测试。

安装:

    准备数据 照常制作第一个列表:Data &gt; Validation 添加脚本,设置一些变量 完成!

准备数据

数据看起来像一个包含所有可能变体的单个表。它必须位于单独的工作表上,以便脚本可以使用它。看这个例子:

这里我们有四个级别,每个值都重复。请注意,数据右侧的 2 列是保留的,因此请不要在此处键入/粘贴任何数据。


第一个简单的数据验证 (DV)

准备一个唯一值列表。在我们的示例中,它是 Planets 的列表。在工作表上查找包含数据的可用空间,然后粘贴公式:=unique(A:A) 在您的主表上选择第一列,DV 将从这里开始。转到数据 > 验证并选择具有唯一列表的范围。


脚本

将此代码粘贴到脚本编辑器中:

function onEdit(event) 


  // Change Settings:
  //--------------------------------------------------------------------------------------
  var TargetSheet = 'Main'; // name of sheet with data validation
  var LogSheet = 'Data1'; // name of sheet with data
  var NumOfLevels = 4; // number of levels of data validation
  var lcol = 2; // number of column where validation starts; A = 1, B = 2, etc.
  var lrow = 2; // number of row where validation starts
  var offsets = [1,1,1,2]; // offsets for levels
  //                   ^ means offset column #4 on one position right.
  
  // =====================================================================================
  SmartDataValidation(event, TargetSheet, LogSheet, NumOfLevels, lcol, lrow, offsets);
  
  // Change Settings:
  //--------------------------------------------------------------------------------------
  var TargetSheet = 'Main'; // name of sheet with data validation
  var LogSheet = 'Data2'; // name of sheet with data
  var NumOfLevels = 7; // number of levels of data validation
  var lcol = 9; // number of column where validation starts; A = 1, B = 2, etc.
  var lrow = 2; // number of row where validation starts
  var offsets = [1,1,1,1,1,1,1]; // offsets for levels
  // =====================================================================================  
  SmartDataValidation(event, TargetSheet, LogSheet, NumOfLevels, lcol, lrow, offsets);

  




function SmartDataValidation(event, TargetSheet, LogSheet, NumOfLevels, lcol, lrow, offsets) 

  //--------------------------------------------------------------------------------------
  // The event handler, adds data validation for the input parameters
  //--------------------------------------------------------------------------------------
  
  var FormulaSplitter = ';'; // depends on regional setting, ';' or ',' works for US
  //--------------------------------------------------------------------------------------
  
  //	===================================   key variables	 =================================
  //
  //		ss			sheet we change (TargetSheet)
  //			br				range to change
  //			scol			number of column to edit
  //			srow			number of row to edit	
  //			CurrentLevel	level of drop-down, which we change
  //			HeadLevel		main level
  //			r				current cell, which was changed by user
  //			X         		number of levels could be checked on the right
  //
  //		ls			Data sheet (LogSheet)
  //
  //    ======================================================================================

// Checks
var ts = event.source.getActiveSheet();
var sname = ts.getName(); 
if (sname !== TargetSheet)  return -1;   // not main sheet
// Test if range fits
var br = event.range;
var scol = br.getColumn(); // the column number in which the change is made
var srow = br.getRow() // line number in which the change is made
var ColNum = br.getWidth();

if ((scol + ColNum - 1) < lcol)  return -2;   // columns... 
if (srow < lrow)  return -3;  // rows
// Test range is in levels
var columnsLevels = getColumnsOffset_(offsets, lcol); // Columns for all levels	
var CurrentLevel = getCurrentLevel_(ColNum, br, scol, columnsLevels);
if(CurrentLevel === 1)  return -4;  // out of data validations
if(CurrentLevel > NumOfLevels)  return -5;  // last level	


/*
	ts - sheet with validation, sname = name of sheet
	
					 NumOfLevels = 4                     
					 offsets = [1,1,1,2] - last offset is 2 because need to skip 1 column
					 columnsLevels = [4,5,6,8] - Columns of validation
					 
									 Columns 7 is skipped
									 |
	   1	2	 3	  4    5    6    7    8    9    
	|----+----+----+----+----+----+----+----+----+
1	|	 |    |    |    |    |    |  x |    |    |
	|----+----+----+----+----+----+----+----+----+
2	|	 |    |    |  v |  V |  ? |  x |  ? |    | lrow = 2 - number of row where validation starts
	|----+----+----+----+----+----+----+----+----+
3	|	 |    |    |    |    |    |  x |    |    |
	|----+----+----+----+----+----+----+----+----+
4	|	 |    |    |    |    |    |  x |    |    |
	|----+----+----+----+----+----+----+----+----+
				   |  |	  |	    |           |
				   |  |	  |	    | Currentlevel = 3 - the number of level to change
				   |  |	  |	                |
				   |  |   | br - cell, user changes: scol - column, srow - row,
				   |  |          ColNum = 1 - width   
				   |__|________   _.....____|
					  |         v
					  |  Drop-down lists					
					  |
					  | lcol = 4 - number of column where validation starts
*/
// Constants
var ReplaceCommas = getDecimalMarkIsCommaLocals(); // // ReplaceCommas = true if locale uses commas to separate decimals
var ls = SpreadsheetApp.getActive().getSheetByName(LogSheet); // Data sheet       				         
var RowNum = br.getHeight();
/* 	Adjust the range 'br' 
    ???       !
	xxx       x
	xxx       x 
	xxx  =>   x
	xxx       x
	xxx       x
*/	
br = ts.getRange(br.getRow(), columnsLevels[CurrentLevel - 2], RowNum); 
// Levels
var HeadLevel = CurrentLevel - 1; // main level
var X = NumOfLevels - CurrentLevel + 1; // number of levels left       
// determine columns on the sheet "Data"
var KudaCol = NumOfLevels + 2;
var KudaNado = ls.getRange(1, KudaCol);  // 1 place for a formula
var lastRow = ls.getLastRow();
var ChtoNado = ls.getRange(1, KudaCol, lastRow, KudaCol); // the range with list, returned by a formula

// ============================================================================= > loop >
var CurrLevelBase = CurrentLevel; // remember the first current level



for (var j = 1; j <= RowNum; j++) // [01] loop rows start
    
  // refresh first val  
  var currentRow = br.getCell(j, 1).getRow();      
  loopColumns_(HeadLevel, X, currentRow, NumOfLevels, CurrLevelBase, lastRow, FormulaSplitter, CurrLevelBase, columnsLevels, br, KudaNado, ChtoNado, ReplaceCommas, ts);
 // [01] loop rows end

       




function getColumnsOffset_(offsets, lefColumn)

// Columns for all levels
var columnsLevels = [];
var totalOffset = 0;	
for (var i = 0, l = offsets.length; i < l; i++)
	
	totalOffset += offsets[i];
	columnsLevels.push(totalOffset + lefColumn - 1);
	

return columnsLevels;



function test_getCurrentLevel()

  var br = SpreadsheetApp.getActive().getActiveSheet().getRange('A5:C5');
  var scol = 1;
  
  
  /*
        |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |
  range |xxxxx| 
   dv range |xxxxxxxxxxxxxxxxx|
 levels    1     2     3
  level          2
  
  */
  Logger.log(getCurrentLevel_(1, br, scol, [1,2,3])); // 2
  
  /*
        |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |
  range |xxxxxxxxxxx| 
   dv range |xxxxx|     |xxxxx|     |xxxxx|
 levels    1           2           3
  level                2
  
  */  
  Logger.log(getCurrentLevel_(2, br, scol, [1,3,5])); // 2
  
  /*
        |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |
  range |xxxxxxxxxxxxxxxxx| 
   dv range |xxxxx|                 |xxxxxxxxxxx| 
 levels    1                       2     3
  level                            2
  
  */    
  Logger.log(getCurrentLevel_(3, br, scol, [1,5,6])); // 2
  
  
  /*
        |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |
  range |xxxxxxxxxxxxxxxxx| 
   dv range |xxxxxxxxxxx|                             |xxxxx| 
 levels    1     2                                   3
  level                                              3
  
  */    
  Logger.log(getCurrentLevel_(3, br, scol, [1,2,8])); // 3
  
  
  /*
        |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |
  range |xxxxxxxxxxxxxxxxx| 
   dv range |xxxxxxxxxxxxxxxxx|
 levels    1     2     3
  level                      4 (error)
  
  */    
  Logger.log(getCurrentLevel_(3, br, scol, [1,2,3]));
  
  
  /*
        |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |
  range |xxxxxxxxxxxxxxxxx| 
   dv range                         |xxxxxxxxxxxxxxxxx|
 levels    
  level    1 (error)                      
  
  */    
  Logger.log(getCurrentLevel_(3, br, scol, [5,6,7])); // 1 
  



function getCurrentLevel_(ColNum, br, scol, columnsLevels)

var colPlus = 2; // const
if (ColNum === 1)  return columnsLevels.indexOf(scol) + colPlus; 
var CurrentLevel = -1;
var level = 0;
var column = 0;
for (var i = 0; i < ColNum; i++ )

	column = br.offset(0, i).getColumn();
	level = columnsLevels.indexOf(column) + colPlus;
	if (level > CurrentLevel)  CurrentLevel = level; 

return CurrentLevel;




function loopColumns_(HeadLevel, X, currentRow, NumOfLevels, CurrentLevel, lastRow, FormulaSplitter, CurrLevelBase, columnsLevels, br, KudaNado, ChtoNado, ReplaceCommas, ts)

  for (var k = 1; k <= X; k++)
     
HeadLevel = HeadLevel + k - 1; 
CurrentLevel = CurrLevelBase + k - 1;
var r = ts.getRange(currentRow, columnsLevels[CurrentLevel - 2]);
var SearchText = r.getValue(); // searched text 
X = loopColumn_(X, SearchText, HeadLevel, HeadLevel, currentRow, NumOfLevels, CurrentLevel, lastRow, FormulaSplitter, CurrLevelBase, columnsLevels, br, KudaNado, ChtoNado, ReplaceCommas, ts);
   



function loopColumn_(X, SearchText, HeadLevel, HeadLevel, currentRow, NumOfLevels, CurrentLevel, lastRow, FormulaSplitter, CurrLevelBase, columnsLevels, br, KudaNado, ChtoNado, ReplaceCommas, ts)



  // if nothing is chosen!
  if (SearchText === '') // condition value =''
  
// kill extra data validation if there were 
// columns on the right
if (CurrentLevel <= NumOfLevels) 

  for (var f = 0; f < X; f++) 
  
    var cell = ts.getRange(currentRow, columnsLevels[CurrentLevel + f - 1]);		  
    // clean & get rid of validation
    cell.clear(contentsOnly: true);              
    cell.clear(validationsOnly: true);
    // exit columns loop  
  

return 0;	// end loop this row	
  
  
  
  // formula for values
  var formula = getDVListFormula_(CurrentLevel, currentRow, columnsLevels, lastRow, ReplaceCommas, FormulaSplitter, ts);  
  KudaNado.setFormula(formula);

  
  // get response
  var Response = getResponse_(ChtoNado, lastRow, ReplaceCommas);
  var Variants = Response.length;


  // build data validation rule
  if (Variants === 0.0) // empty is found
  
return;
    
  if(Variants >= 1.0) // if some variants were found
  

var cell = ts.getRange(currentRow, columnsLevels[CurrentLevel - 1]);
var rule = SpreadsheetApp
.newDataValidation()
.requireValueInList(Response, true)
.setAllowInvalid(false)
.build();
// set validation rule
cell.setDataValidation(rule);
      
  if (Variants === 1.0) // // set the only value
        
cell.setValue(Response[0]);
SearchText = null;
Response = null;
return X; // continue doing DV
   // the only value
  
  return 0; // end DV in this row
  



function getDVListFormula_(CurrentLevel, currentRow, columnsLevels, lastRow, ReplaceCommas, FormulaSplitter, ts)

  
  var checkVals = [];
  var Offs = CurrentLevel - 2;
  var values = [];
  // get values and display values for a formula
  for (var s = 0; s <= Offs; s++)
  
var checkR = ts.getRange(currentRow, columnsLevels[s]);
values.push(checkR.getValue());
   		  
  
  var LookCol = colName(CurrentLevel-1); // gets column name "A,B,C..."
  var formula = '=unique(filter(' + LookCol + '2:' + LookCol + lastRow; // =unique(filter(A2:A84

  var mathOpPlusVal = ''; 
  var value = '';

  // loop levels for multiple conditions  
  for (var i = 0; i < CurrentLevel - 1; i++)             
formula += FormulaSplitter; // =unique(filter(A2:A84;
LookCol = colName(i);
		
value = values[i];

mathOpPlusVal = getValueAndMathOpForFunction_(value, FormulaSplitter, ReplaceCommas); // =unique(filter(A2:A84;B2:B84="Text"

if ( Array.isArray(mathOpPlusVal) )

  formula += mathOpPlusVal[0];
  formula += LookCol + '2:' + LookCol + lastRow; // =unique(filter(A2:A84;ROUND(B2:B84
  formula += mathOpPlusVal[1];

else

  formula += LookCol + '2:' + LookCol + lastRow; // =unique(filter(A2:A84;B2:B84
  formula += mathOpPlusVal;



    
  
  formula += "))"; //=unique(filter(A2:A84;B2:B84="Text"))

  return formula;



function getValueAndMathOpForFunction_(value, FormulaSplitter, ReplaceCommas)

  var result = '';
  var splinter = '';	

  var type = typeof value;
  
 
  // strings
  if (type === 'string') return '="' + value + '"';
  // date
  if(value instanceof Date)
  
return ['ROUND(', FormulaSplitter +'5)=ROUND(DATE(' + value.getFullYear() + FormulaSplitter + (value.getMonth() + 1) + FormulaSplitter + value.getDate() + ')' + '+' 
      + 'TIME(' + value.getHours() + FormulaSplitter + value.getMinutes() + FormulaSplitter + value.getSeconds() + ')' + FormulaSplitter + '5)'];	  
    
  // numbers
  if (type === 'number')
  
if (ReplaceCommas)

	return '+0=' + value.toString().replace('.', ',');		

else

	return '+0=' + value;

  
  // booleans
  if (type === 'boolean')
  
  return '=' + value;
    
  // other
  return '=' + value;




function getResponse_(allRange, l, ReplaceCommas)

  var data = allRange.getValues();
  var data_ = allRange.getDisplayValues();
  
  var response = [];
  var val = '';
  for (var i = 0; i < l; i++)
  
val = data[i][0];
if (val !== '') 

  var type = typeof val;
  if (type === 'boolean' || val instanceof Date) val = String(data_[i][0]);
  if (type === 'number' && ReplaceCommas) val = val.toString().replace('.', ',')
  response.push(val);  

  
  
  return response;  





function colName(n) 
var ordA = 'a'.charCodeAt(0);
var ordZ = 'z'.charCodeAt(0);

var len = ordZ - ordA + 1;

var s = "";
while(n >= 0) 
    s = String.fromCharCode(n % len + ordA) + s;
    n = Math.floor(n / len) - 1;

return s; 



function getDecimalMarkIsCommaLocals() 


// list of Locals Decimal mark = comma
var LANGUAGE_BY_LOCALE = 
af_NA: "Afrikaans (Namibia)",
af_ZA: "Afrikaans (South Africa)",
af: "Afrikaans",
sq_AL: "Albanian (Albania)",
sq: "Albanian",
ar_DZ: "Arabic (Algeria)",
ar_BH: "Arabic (Bahrain)",
ar_EG: "Arabic (Egypt)",
ar_IQ: "Arabic (Iraq)",
ar_JO: "Arabic (Jordan)",
ar_KW: "Arabic (Kuwait)",
ar_LB: "Arabic (Lebanon)",
ar_LY: "Arabic (Libya)",
ar_MA: "Arabic (Morocco)",
ar_OM: "Arabic (Oman)",
ar_QA: "Arabic (Qatar)",
ar_SA: "Arabic (Saudi Arabia)",
ar_SD: "Arabic (Sudan)",
ar_SY: "Arabic (Syria)",
ar_TN: "Arabic (Tunisia)",
ar_AE: "Arabic (United Arab Emirates)",
ar_YE: "Arabic (Yemen)",
ar: "Arabic",
hy_AM: "Armenian (Armenia)",
hy: "Armenian",
eu_ES: "Basque (Spain)",
eu: "Basque",
be_BY: "Belarusian (Belarus)",
be: "Belarusian",
bg_BG: "Bulgarian (Bulgaria)",
bg: "Bulgarian",
ca_ES: "Catalan (Spain)",
ca: "Catalan",
tzm_Latn: "Central Morocco Tamazight (Latin)",
tzm_Latn_MA: "Central Morocco Tamazight (Latin, Morocco)",
tzm: "Central Morocco Tamazight",
da_DK: "Danish (Denmark)",
da: "Danish",
nl_BE: "Dutch (Belgium)",
nl_NL: "Dutch (Netherlands)",
nl: "Dutch",
et_EE: "Estonian (Estonia)",
et: "Estonian",
fi_FI: "Finnish (Finland)",
fi: "Finnish",
fr_BE: "French (Belgium)",
fr_BJ: "French (Benin)",
fr_BF: "French (Burkina Faso)",
fr_BI: "French (Burundi)",
fr_CM: "French (Cameroon)",
fr_CA: "French (Canada)",
fr_CF: "French (Central African Republic)",
fr_TD: "French (Chad)",
fr_KM: "French (Comoros)",
fr_CG: "French (Congo - Brazzaville)",
fr_CD: "French (Congo - Kinshasa)",
fr_CI: "French (Côte d’Ivoire)",
fr_DJ: "French (Djibouti)",
fr_GQ: "French (Equatorial Guinea)",
fr_FR: "French (France)",
fr_GA: "French (Gabon)",
fr_GP: "French (Guadeloupe)",
fr_GN: "French (Guinea)",
fr_LU: "French (Luxembourg)",
fr_MG: "French (Madagascar)",
fr_ML: "French (Mali)",
fr_MQ: "French (Martinique)",
fr_MC: "French (Monaco)",
fr_NE: "French (Niger)",
fr_RW: "French (Rwanda)",
fr_RE: "French (Réunion)",
fr_BL: "French (Saint Barthélemy)",
fr_MF: "French (Saint Martin)",
fr_SN: "French (Senegal)",
fr_CH: "French (Switzerland)",
fr_TG: "French (Togo)",
fr: "French",
gl_ES: "Galician (Spain)",
gl: "Galician",
ka_GE: "Georgian (Georgia)",
ka: "Georgian",
de_AT: "German (Austria)",
de_BE: "German (Belgium)",
de_DE: "German (Germany)",
de_LI: "German (Liechtenstein)",
de_LU: "German (Luxembourg)",
de_CH: "German (Switzerland)",
de: "German",
el_CY: "Greek (Cyprus)",
el_GR: "Greek (Greece)",
el: "Greek",
hu_HU: "Hungarian (Hungary)",
hu: "Hungarian",
is_IS: "Icelandic (Iceland)",
is: "Icelandic",
id_ID: "Indonesian (Indonesia)",
id: "Indonesian",
it_IT: "Italian (Italy)",
it_CH: "Italian (Switzerland)",
it: "Italian",
kab_DZ: "Kabyle (Algeria)",
kab: "Kabyle",
kl_GL: "Kalaallisut (Greenland)",
kl: "Kalaallisut",
lv_LV: "Latvian (Latvia)",
lv: "Latvian",
lt_LT: "Lithuanian (Lithuania)",
lt: "Lithuanian",
mk_MK: "Macedonian (Macedonia)",
mk: "Macedonian",
naq_NA: "Nama (Namibia)",
naq: "Nama",
pl_PL: "Polish (Poland)",
pl: "Polish",
pt_BR: "Portuguese (Brazil)",
pt_GW: "Portuguese (Guinea-Bissau)",
pt_MZ: "Portuguese (Mozambique)",
pt_PT: "Portuguese (Portugal)",
pt: "Portuguese",
ro_MD: "Romanian (Moldova)",
ro_RO: "Romanian (Romania)",
ro: "Romanian",
ru_MD: "Russian (Moldova)",
ru_RU: "Russian (Russia)",
ru_UA: "Russian (Ukraine)",
ru: "Russian",
seh_MZ: "Sena (Mozambique)",
seh: "Sena",
sk_SK: "Slovak (Slovakia)",
sk: "Slovak",
sl_SI: "Slovenian (Slovenia)",
sl: "Slovenian",
es_AR: "Spanish (Argentina)",
es_BO: "Spanish (Bolivia)",
es_CL: "Spanish (Chile)",
es_CO: "Spanish (Colombia)",
es_CR: "Spanish (Costa Rica)",
es_DO: "Spanish (Dominican Republic)",
es_EC: "Spanish (Ecuador)",
es_SV: "Spanish (El Salvador)",
es_GQ: "Spanish (Equatorial Guinea)",
es_GT: "Spanish (Guatemala)",
es_HN: "Spanish (Honduras)",
es_419: "Spanish (Latin America)",
es_MX: "Spanish (Mexico)",
es_NI: "Spanish (Nicaragua)",
es_PA: "Spanish (Panama)",
es_PY: "Spanish (Paraguay)",
es_PE: "Spanish (Peru)",
es_PR: "Spanish (Puerto Rico)",
es_ES: "Spanish (Spain)",
es_US: "Spanish (United States)",
es_UY: "Spanish (Uruguay)",
es_VE: "Spanish (Venezuela)",
es: "Spanish",
sv_FI: "Swedish (Finland)",
sv_SE: "Swedish (Sweden)",
sv: "Swedish",
tr_TR: "Turkish (Turkey)",
tr: "Turkish",
uk_UA: "Ukrainian (Ukraine)",
uk: "Ukrainian",
vi_VN: "Vietnamese (Vietnam)",
vi: "Vietnamese"



var SS = SpreadsheetApp.getActiveSpreadsheet();
var LocalS = SS.getSpreadsheetLocale();


if (LANGUAGE_BY_LOCALE[LocalS] == undefined) 
  return false;
  

  //Logger.log(true);
  return true;


/*
function ReplaceDotsToCommas(dataIn) 
  var dataOut = dataIn.map(function(num) 
  if (isNaN(num)) 
    return num;
      
  num = num.toString();
  return num.replace(".", ",");
  );
  return dataOut;

*/

这是一组要更改的变量,您可以在脚本中找到它们:

  var TargetSheet = 'Main'; // name of sheet with data validation
  var LogSheet = 'Data2'; // name of sheet with data
  var NumOfLevels = 7; // number of levels of data validation
  var lcol = 9; // number of column where validation starts; A = 1, B = 2, etc.
  var lrow = 2; // number of row where validation starts
  var offsets = [1,1,1,1,1,1,1]; // offsets for levels

我建议所有熟悉脚本的人将您的编辑发送到此代码。我想,有更简单的方法可以找到验证列表并使脚本运行得更快。

【讨论】:

我们尝试了您的脚本 - 命名为 Current version- 并设法破坏了它。 :) 我们不明白怎么做。我们在这里发布sheet itself 的副本,以便您进一步了解。你能帮我吗? 现在只是对您的帖子的建议:您有 2 个版本。 CurrentTest version 2016-03-14. Improved:...。如果Test version 得到改进,则 this 应该是当前的,而不是相反。或者,您可以在帖子中添加 EDIT,如您所见 here 感谢您的评论。请尝试latest version。我也更新了我的答案。如果你在这个版本中发现错误,我会检查它。 感谢您的快速回复。我们测试了您最新的v.3 的副本。这当然是一个改进的版本。做得好!仍然。我们再次破坏了它。 :) 我想我发现了我们是如何为v.2v.3 管理它的。 我们添加了更多数据(行星、国家等),然后我们整理出Ascending Order -&gt;A-Z中的表格。之后就不行了。我还在MainData 上将Freeze 用于Title 行,但仍然存在问题。 由于我们能够添加新数据,如果我们可以 - 在任何时候 - Sort 两个或任何一个工作表,那将是理想的 有什么想法吗? 我认为我们发现的另一个错误可能与数字中的位置和小数点有关(4.34,3)。还有组合:Tatooine-Yulab-555.55 由于某种原因有问题。请看this sheet @datanovice,这是latest version of the script。该脚本创建一个过滤器公式,不同的国家使用不同的小数分隔符(点或逗号)。语言 locale 是获取以逗号为分隔符的 locale 列表。

以上是关于您如何在 Google 表格中进行动态/依赖下拉菜单?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Struts 2 中进行动态 URL 重定向?

使用gorm golang在多个表中进行动态列搜索

如何在红移中进行动态正则表达式匹配?

使用 XMLRPC 在 Python 中进行动态函数调用

在CodeIgniter中进行动态路由

使用 HTML 输入在 D3 中进行动态过滤