LoginSignup
6
7

More than 1 year has passed since last update.

[GAS]二次元配列からオブジェクト/MAPオブジェクトに変換する (MapとReduceで)

Last updated at Posted at 2022-06-08

想定している状況

  • スプレッドシートのデータをGASで取得して処理したい
  • ただしフォーマットが確定していない
  • あとからカラムの追加や移動があるかもしれない

A「1列目ID、2列目TYPE、3列目DATE…やな!」
B「ごめん、IDのとなりにNAMEが必要やったわ!」
A「できたで。1列目ID、2列目NAME、3列目TYPE、4列目DATE…やな!」
B「先日のNAMEやけどな、FAMILY NAMEとLAST NAMEに分けたいねん!」
A「…。」

列番号指定でスクリプトを動かすのはやめて、オブジェクト化してカラム名で扱いましょう!

console.log(array[1]) //なにが入っているかわからない、列が移動すると困る
console.log(object["name"]) //わかる

目次

ダミーデータ
二次元配列を取得する
オブジェクトにする
MAPオブジェクトにする
おまけ…Python(gspread)の場合

ダミーデータ

ダミーデータを用意しました。1行目がヘッダー、2行目以降にレコードが続きます。

二次元配列

まずはgetValues();で二次元配列を取得します。

  const id = "XXXXXXXXXXXXXXXX";
  const ss = SpreadsheetApp.openById(id);
  const sheet = ss.getSheetByName('DUMMY_personal_infomation');

  // 二次元配列を取得する
  const values = sheet.getDataRange().getValues();

  console.log(values)

スクリーンショット 2022-05-25 11.02.19.png

オブジェクト

二次元配列をオブジェクトにしてみましょう。

  // 二次元配列を取得する
  const values = sheet.getDataRange().getValues();

  // オブジェクトにする
  const [header, ...records] = values;  //1行目がヘッダー、以降がレコード
  const objects = records.map(
    record => record.reduce((acc, value, index) => {
      acc[header[index]] = value;
      return acc;
    }, {})
  );

  console.log(objects)
  • 二次元配列のvalues[0]がヘッダー、[1]以降がレコードにあたります。
  • values.shift();で最初の要素を削除してもよいですが、スプレッド構文でも最初の要素(header)と、以降の要素(records)に分けられます。
  • あとは行数recordsの数だけループして処理していけばよいでしょう。
  • reduceのaccは「直前の処理で返された値」を持つので、初期値「 {}」に対して順々にプロパティを追加していきます。

なお、プロパティの追加にスプレッド構文を使うとさらに短くなります。

  const values = sheet.getDataRange().getValues();

  const [header, ...records] = values;
  const objects = records.map(
      record => record.reduce((acc, value, index) => ({...acc, [header[index]]:value}), {})
    );  

MAPオブジェクト

二次元配列をMAPオブジェクトにしましょう。

  // 二次元配列を取得する
  const values = sheet.getDataRange().getValues();

  // MAPオブジェクトにする
  const [header, ...records] = values;
  const maps = records.map(
    record => record.reduce((acc, value, index) => acc.set(header[index], value), new Map())
  );

  for (m of maps) {
    console.log(...m);
  }

スクリーンショット 2022-05-25 11.16.24.png

  • Mapオブジェクトもオブジェクトと同様にキーと値のペアを保持します。
  • Arrayで使用されるMapメソッドとは異なるため注意。ややこしいですね。
  • またMapオブジェクトは、要素を追加したり取得したりするメソッドが用意されています。
  • new Map();で空のMAPオブジェクトを生成して、初期値としてreduceに渡します。
  • set(key, value);で要素を追加します。

オブジェクトでスプレッド構文を駆使するより、専用メソッドのあるMAPオブジェクトの方が処理がわかりやすい…でしょうか?

おまけ…Python

Pythonの辞書型で取得するには、gspreadライブラリを使用してworksheet.get_all_records()の1行でよい…

from google.colab import auth
from google.auth import default
import gspread
from pprint import pprint #Dictを整形してきれいに出力できる

# 認証
auth.authenticate_user()
creds, _ = default()
gc = gspread.authorize(creds)

workbook = gc.open_by_key('XXXXXXXXXXXXXXXX')
worksheet = workbook.worksheet('DUMMY_personal_infomation')

# worksheetの全データをdictで取得
recordsDict = worksheet.get_all_records() 

pprint(recordsDict)
6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7