列文字の代わりに列ヘッダーでクエリを使用する


7

シートの列ヘッダーである1次元配列の列文字を動的に取得するために使用する数式があります。文字ではなく列名でQUERY()を使用するため。

SUBSTITUTE(ADDRESS(1,MATCH("Weight", Headers, 0),4), "1", "")

構文はかなりのスペースを必要とし、クエリが長くなると、見た目に困難になる場合があります。例:

=QUERY(A3:F13, "select "&SUBSTITUTE(ADDRESS(1,MATCH("Type", Headers, 0),4), "1", "")&" where ("&SUBSTITUTE(ADDRESS(1,MATCH("Version", Headers, 0),4), "1", "")&" = 'Version 1') and ("&SUBSTITUTE(ADDRESS(1,MATCH("Type", Headers, 0),4), "1", "")&" <> 'Type')")

と同じです

=QUERY(A3:F13, "select E where (A = 'Version 1') and (E <> 'Type')")

常に行を選択できるという利点があるので、それらがどのような順序であっても必要です。

アプリスクリプトでホイールを再作成せずに、次のように動作する関数を作成することはできますか?

GetHeaders(range, string)

=QUERY(A3:F13, "select "&GetHeader(Headers, "Type")&" where ("&GetHeader(Headers, "Version")&" = 'Version 1') and ("&GetHeader(Headers, "Type")&" <> 'Type')")

@Normal Humanが回答のコメントで説明したように、カスタム関数を作成する唯一の方法は、Appsスクリプトを使用することです。それを使用せずに回答が必要な場合は、「関数を作成」を言い換え(つまり「式を作成」)し、カスタム関数の使用は望ましくないことを述べてください。
ルベン

回答:


4

Appsスクリプトなし

Apps Scriptなしで新しい関数を作成することはできません。プロセスを合理化する唯一の方法は、より多くのセルをどこかに配置し、それらを参照することです。

例:「Weight」などの各ヘッダーの下に、次のコマンドを入力します

=regexextract(address(1, column()), "[A-Z]+")

これにより、列文字がヘッダーの下に配置されます。ヘッダーの名前付き範囲に文字のある行を含めます。クエリ文字列では、

hlookup("Weight", Headers, 2, 0)

目には簡単です

SUBSTITUTE(ADDRESS(1,MATCH("Weight", Headers, 0),4), "1", "")

Apps Scriptを使用

範囲(名前付きまたはそれ以外)をカスタム関数に渡すと、が渡されます。シート内の位置に関する情報はありません。しかし、私は解決策を見つけました:の最初の引数からヘッダーの場所を推測しますquery。それ自体繰り返しだった「ヘッダー」を含める必要はありません。

バージョン1:クエリ範囲に含まれるヘッダー

クエリに渡される範囲にヘッダー行を含めることを好み、ヘッダー行の数をの3番目の引数として指定しますquery。これにより、データをヘッダーとして誤って解釈すること、またはその逆を回避できます。例えば:

=QUERY(A1:F13, "select "&GetHeader("Type")&" where ("&GetHeader("Version")&" = 'Version 1') and ("&GetHeader("Type")&" <> 'Type')", 2) 

上記のクエリで使用するカスタム関数は次のとおりです。

function getHeader(name) {
  var sheet = SpreadsheetApp.getActiveSheet();
  var formula = SpreadsheetApp.getActiveRange().getFormula();
  var args = formula.match(/\w+:\w+(?=[ ,])/);
  var range = sheet.getRange(args[0]);
  var firstRow = range.offset(0, 0, 1, range.getWidth());
  var headers = firstRow.getValues();
  for (var i = 0; i < headers[0].length; i++) {
    if (headers[0][i] == name) {
      var notation = range.getCell(1, i+1).getA1Notation();
      var column = notation.replace(/\d/, '');
      return column;
    }
  }
  return 'Not found';
}

関数は、呼び出し元のセルから数式を取得します。正規表現で数式の最初の範囲引数を抽出します。次に、指定された文字列を検索して、ヘッダー行であると想定して、この範囲の最初の行を調べます。文字列を含むセルのA1表記を取得し、その行部分を削除して、結果を返します。

バージョン2:行1から取得されたヘッダー

クエリの引数の行に関係なく、ヘッダーがシートの最初の行から取得される代替バージョン。交換するだけ

  var firstRow = range.offset(0, 0, 1, range.getWidth());

  var firstRow = sheet.getRange(1, range.getColumn(), 1, range.getWidth());

このバージョンでは、使用できます

=QUERY(A3:F13, "select "&GetHeader("Type")&" where ("&GetHeader("Version")&" = 'Version 1') and ("&GetHeader("Type")&" <> 'Type')")  

ノーマル人間に感謝!ただし、カスタム関数は大量に使用すると断続的にしか機能しない傾向があるため、毎回機能するものが必要であり、経験上、カスタム関数が毎回機能するとは限らないため(Loading...解決可能な永続的な問題は、関数の削除と再貼り付け)。
Douglas Gaskell

1
「関数を作成する」ように頼まれました... Apps Scriptを使用することが新しい関数を作成する唯一の方法です。それ以外の場合は、組み込み関数の同じ組み合わせをコピーして貼り付けるだけです。パフォーマンスに関する懸念は理解していますが、通常はクエリの数がそれほど多くないか、クエリ自体に関連するパフォーマンスの問題があります。

1
とはいえ、スクリプトなしで対処するための提案を追加しました。

0

私はuser79865が提供する答えが本当に好きです。これを使用して、ロケールが米国(ポーランド語など)と異なる場合は、シートの関数引数に異なるセパレータを使用している可能性があります。そして代わりに:

=QUERY(A3:F13, "select "&GetHeader("Type")&" where ("&GetHeader("Version")&" = 'Version 1') and ("&GetHeader("Type")&" <> 'Type')", 2)

あなたが持っているかもしれません(の;代わりに,):

=QUERY(A3:F13; "select "&GetHeader("Type")&" where ("&GetHeader("Version")&" = 'Version 1') and ("&GetHeader("Type")&" <> 'Type')"; 2)

このような場合、関数の正規表現(バージョン1)の4行目を次のように変更する必要があります。

var args = formula.match(/\w+:\w+(?=[ ,])/);

に:

var args = formula.match(/\w+:\w+(?=[ ;])/);


最後に、データが別のシート(同じスプレッドシート内)にある場合は、正規表現を変更してシート名を検索し、それを使用して列名を検索できます。

function getHeader( name ) {
  var sheet = SpreadsheetApp.getActiveSheet();
  var formula = SpreadsheetApp.getActiveRange().getFormula();
  var args = formula.match(/\((\'*(.+(?=[!']))\'*!)*(\w+:\w+(?=[ ;]))/); // 1: "'sheet name'!", 2:"sheet name", 3:"A1:D100"
  if( args[2] ) 
    sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(args[2]);
  var range = sheet.getRange(args[3]);
  var firstRow = range.offset(0, 0, 1, range.getWidth());
  var headers = firstRow.getValues();
  for (var i = 0; i < headers[0].length; i++) {
    if (headers[0][i] == name) {
      var notation = range.getCell(1, i+1).getA1Notation();
      var column = notation.replace(/\d/, '');
      return column;
    }
  }
  return 'Not found';
}


コメントを追加したいのですが、初心者はコメントできません...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.