2014年3月4日火曜日

Google スプレッドシートのカスタム関数に「列」(範囲)を引数として与えた場合、目的の型に変換する必要がある

1. 日付の「列」をカスタム関数に与えたら、 Date 型メソッドの呼び出しで TypeError

a. 日付が入力された「セル」をカスタム関数に与えたら、Date 型メソッドを呼び出せる

Google スプレッドシートで、日付から「日」を得るカスタム関数を以下のように定義した。

function getDate(date){
  return date.getDate();
}

シートの A 列に日付を入力し、B1 セルで上記のカスタム関数を呼び出す。そして、下方向へコピーした。

SnapCrab_No-0838

その結果、B 列に「日」を表示することができた。

 

b. 日付が入力された「列」をカスタム関数に与えたら、Date 型メソッドを呼び出せない

次に、上記の「カスタム関数呼び出しの繰り返し」を配列数式で置きかえるために、以下のように関数を定義しした。

function getDateList(dates){
  var i, result = [];
  for (i = 0; i < dates.length; i++){
    result[i] = [dates[i].getDate()];
  }
  return result;
}

getDateList 関数を B1 セルで呼び出した結果、TypeError が表示された。

SnapCrab_No-0840

TypeError - JavaScript | MDN とは、

値が期待される型でない場合のエラーを表します。

先ほどの getDate 関数は、日付が入力された「1つのセル」を引数として与えた。関数の中で getDate() メソッド を呼び出すとき、「日付型の値に対してメソッドを呼び出す」と考えてコードを記述した。

これに対して、getDateList 関数では、「日付の列」を与えている。関数の中では、各要素に対して getDate() メソッド を呼び出した。しかし、結果、そのようなメソッドはオブジェクトに定義されていないというエラーが表示された。

どうやら、カスタム関数に「列」(範囲)を引数として与えると、各要素の型情報が失われるようだ。

 

c. JavaScript の配列と比較

JavaScript では日付型の要素を持つ配列の要素に対して、getDate() メソッド を呼び出すことができる。

[
    new Date('2014/1/1'),
    new Date('2014/1/2'),
    new Date('2014/1/3')
].map(function (x) {
    return x.getDate();
});

実行すると、結果は以下のように表示される。

[1, 2, 3]

つまり、Google スプレッドシートで、カスタム関数に「列」(範囲)を引数として与えることは、JavaScript の配列を与えることとは異なる。

 

2. カスタム関数において列(範囲)から要素を取り出したら、目的の型に変換する必要がある

Google スプレッドシートで上記の TypeError を回避するためには、「列」(範囲)から要素を取り出すときに、日付型に変換する必要がある。

function getDateList(dates){
  var i, result = [];
  for (i = 0; i < dates.length; i++){
    result[i] = [new Date(dates[i]).getDate()];
  }
  return result;
}

これにより、Date 型のメソッドを呼び出すことができるようになる。

SnapCrab_No-0842

 

3. シート上の計算と、カスタム関数、配列数式における「文字列」「数値」「日付」の扱われ方の比較

上記の点について、Google スプレッドシートで「文字列」「数値」「日付」がどのように扱われるのか確認しておく。

a. 表計算のシート上で 1 を足す

最初に、表計算のシート上における計算について確認する。

対象の値は、

  • 文字列 「ほげ
  • 数値 `100
  • 日付 `2014/1/1

各値に対して + 演算子を使い、「1を足す」計算を行う。

SnapCrab_No-0843

結果、数値は 1 加算され、日付は1日後が表示された。

ただし、文字列に対して 1 を足すことはできない旨のエラーが表示された。

 

b. 単一のセルを与えるカスタム関数で + 1

次に、セルの値に対して + 1 するカスタム関数を定義する。

function addOne(cell){
 return cell + 1; 
}

この関数を呼び出した結果、文字列 「ほげ」 に `1’ が連結された。これは JavaScript の仕様で、文字列に + 演算子を適用すると文字列の連結となるから。

数値は `100’ に 1 が足され 101 となった。JavaScript では数値に + 演算子を適用すると「加算」が行われる。

「日付」は、日付の文字列表現に 1 が連結された。

SnapCrab_No-0844

このような結果となる理由を探るために、各セルの値の型を検査する関数を定義した。

function testType(obj) {
  return typeof obj
}

結果、文字列は string、数値は number、日付は object となった。

SnapCrab_No-0846

日付の型は object と表示されたが、Date 型のメソッドを呼び出すことができる。JavaScript で日付を加算するには、以下のように記述する。

function addOneDate(cell){
  var d = new Date(cell);
  d.setDate(d.getDate() + 1);
  return d;
}

この関数を呼び出すと、引数に渡した翌日(1日後)が表示される。

SnapCrab_No-0845

 

c. 列(範囲)を与えるカスタム関数で + 1

上記の + 1 する「カスタム関数の繰り返し」を配列数式で置きかえるために、以下の関数を定義した。

function addOneList(col){
  var i, result = [];
  for(i = 0; i < col.length; i++){
    result[i] = [col[i] + 1]; 
  }
  return result;
}

この関数を呼び出した結果、先ほどと結果が異なった。さっきは数値が数値として扱われ + 1 が加算されたが、今回は数値が文字列として扱われ “100” に “1” が連結される形となった。

SnapCrab_No-0847

つまり、ここでもカスタム関数に「列」(範囲)を与えると、型情報が失われたことが分かる。

配列数式で型を検査する関数を定義して、型を確認してみる。

function testTypeList(col){
  var i, result = [];
  for(i = 0; i < col.length; i++){
    result[i] = [typeof col[i]]; 
  }
  return result;
}

この結果、全て object となった。やはり、カスタム関数に「列」(範囲)を与えた場合、自分で目的の型に変換しなければならないようだ。

SnapCrab_No-0848

 

d. 列(範囲)を与えるカスタム関数で数値を扱う

例えば、A 列に数値があり、B 列に + 1 した値を得たいとする。

このとき、配列数式でカスタム関数を記述する場合、列から要素を取り出した後、数値にキャストする必要がある。

function addOneNumberList(col){
  var i, result = [];
  for(i = 0; i < col.length; i++){
    result[i] = [Number(col[i]) + 1]; 
  }
  return result;
}

これにより、数値による計算が行われる。SnapCrab_No-0849

数値にキャストしない場合、+ 演算子が文字列の連結として扱われてしまう。

SnapCrab_No-0850