見出し画像

【GAS】Google Apps Script 活用事例 連想配列からデータを取り出す勉強

ログで何の値を取り出しているのかを把握しやすいため、オブジェクト、連想配列をよく使うようになりました。しかし、スプレッドシートに貼り付ける際などに2次元配列に変換する必要があります。

他のサイトで見つけた、バグがない綺麗なコードをコピペして使い回すだけだと、なぜそうなるかが分からないので、自分の手を動かして、実験してみました。自分の落書き帳のような感じです。

基本形

function myFunction() {
 const users = [
   { name:'太郎', age:32, tel:'080-1234-5678' },
   { name:'花子', age:24, tel:'050-9876-5432' }
 ];

 const keys  = Object.keys(users[0]);
 const index = Object.keys(users[0]['name']);


 console.log(index); //[['0', '1']]
 console.log(users[0].name); //太郎

 console.log(keys); //[ 'name', 'age', 'tel' ]
 console.log(users[0]['name']); //太郎
 //keysのオブジェクトを、valueと入れ替えている。
 //const values = users.map(user =>  keys.map(key => user[key])); 

}

Object.keys

これが、自分の中では一番分かりやすいベストプラクティスです。
keysのオブジェクトをmapで、keyからvalueに総入れ替えするみたいなイメージです。

function converValues() {
 const users = [
   { name:'太郎', age:32, tel:'080-1234-5678' },
   { name:'花子', age:24, tel:'050-9876-5432' },
 ];

 const keys   = Object.keys(users[0]); //[ 'name', 'age', 'tel' ]
 const values = users.map(user =>  keys.map(key => user[key]));

 console.log(values);

}

蛇足・reduceとkeysの組み合わせ

  const header = values[0];
  //console.log(values);

  const column = {
    status:    header.indexOf('ステータス'),
    name:      header.indexOf('名前'),
    hrmos:     header.indexOf('HRMOS'),
    lastName:  header.indexOf('セイ'),
    firstName: header.indexOf('メイ'),
    phone:     header.indexOf('電話番号'),
    mail:      header.indexOf('メールアドレス'),
    school:    header.indexOf('学校名'),
    major:     header.indexOf('学部、学科、専攻、コース'),
  }

  const keys   = Object.keys(column);
  const numbers = keys.map(key => column[key]);
  
  console.log(keys);
  console.log(numbers);

  //数値が格納された配列の中にindexと同じ数値が含まれるかをチェック
  const newValues = values.map(array => array.reduce((accumulator, current, index) =>{
      if(numbers.includes(index)){
        accumulator.push(current);
      }
    return accumulator
    }, [])//reduce
  );//map

  //console.log(newValues);

  //activeかつHRMOSが空白
  const filtered = newValues.filter(array => array.includes('active') && !array[2]);
  console.log(filtered);

}

シート上の欲しい列だけを取り出すような処理を実務で頻繁に使っています。

サンプルコード

function myFunction() {
  const values = [
    ['応募日時', 'ID', '名前', '応募職種'],
    ['2022/02/28', 'JP0475' ,'野比のび太', '事務'],
    ['2022/02/24', 'JP0473' ,'スネ夫', '企画'],
    ['2022/02/21', 'JP0469' ,'たけし', '営業']
  ]
  const header = values.shift();
  const column = {
    id:   header.indexOf('ID'),
    name: header.indexOf('名前'),
  }
  const keys   = Object.keys(column);
  const numbers = keys.map(key => column[key]);
  console.log(numbers);

  const newValues = values.map(array => array.reduce((accumulator, current, index) =>{
      if(numbers.includes(index)){
        accumulator.push(current);
      }
    return accumulator
    }, [])//reduce
  );//map

  console.log(newValues);
}/

/[ [ 'JP0475', '野比のび太' ],

reduce

function convertValues() {
 const data = [
   { id: 1, name: 'Taro', age: 20, country: 'Japan'},
   { id: 2, name: 'Yan', age: 30, country: 'China'},
   { id: 3, name: 'Bob', age: 40, country: 'America'},
 ];

 // data 内にある各要素のオブジェクトのキーを、name と country だけにしたい
 // やっている事はシンプルで配列にpushしているだけ。 for文や、配列の変数宣言が不要
 
 const reducer = (accumulator, current) => {
   const { name, country } = current;
   accumulator.push([ name, country ]);
   return accumulator;
 };

 const result  = data.reduce(reducer, []);
 console.log(result); //[ [ 'Taro', 'Japan' ], [ 'Yan', 'China' ], [ 'Bob', 'America' ] ]

}

ログを表示させて確かめてみると....

function convertValues() {
 const data = [
   { id: 1, name: 'Taro', age: 20, country: 'Japan'},
   { id: 2, name: 'Yan', age: 30, country: 'China'},
   { id: 3, name: 'Bob', age: 40, country: 'America'},
 ];

 console.log(data[0].name); //Taro
 console.log(data[1].name); //Yan
 console.log(data[2].name); //Bob

 console.log(data[0].country); //Taro
 console.log(data[1].country); //Yan
 console.log(data[2].country); //Bob
 
 //reduceで配列内の要素全てに対し、.name .countryだけを新たな配列に加える事ができる。

}

for文も配列の変数宣言も不要.....す、すげぇ......となったスクリプト
下記のブログで見つけました。

これの応用

function myFunction() {
 const array1 = [1, 2, 3, 4];
 const reducer = (accumulator, currentValue) => accumulator + currentValue;

 console.log(array1.reduce(reducer, 5));
 // 5 + 1 + 2 + 3 + 4
 // expected output: 15
}

Object.entries

2次元配列で返ってきてしまうのがネック。同じ事をして、concatで繋ぐとか
ってか、花子ォォォ〜!!

function convertValues() {
 const users = [
  { name:'太郎', age:32, tel:'080-1234-5678' },
  { name:'花子', age:24, tel:'050-9876-5432' },
];

 //1次元配列の最初の要素keyを削除する。
 let originalValues = Object.entries(users[0]);//[ [ 'name', '太郎' ], [ 'age', 32 ], [ 'tel', '080-1234-5678' ] ]
 console.log('originalValues\n\n', originalValues);

 originalValues.map(array => array.splice(0, 1));

 //1次元配列に変換
 const array   = originalValues.flat();
 let newValues = [];
 newValues.push(array);

 console.log('array: ', array);//[ '太郎', 32, '080-1234-5678' ]
 console.log('newValues: ', newValues);//[ [ '太郎', 32, '080-1234-5678' ] ]

}

keyが要らないけど、変換出来ない事もない。

function convertValues() {
 const users = [
   { name:'太郎', age:32, tel:'080-1234-5678' },
   { name:'花子', age:24, tel:'050-9876-5432' },
 ];

 const originalValues = users.map(user => Object.entries(user).flat());
 console.log('originalValues\n\n', originalValues);

}

/*
[ 
 [ 'name', '太郎', 'age', 32, 'tel', '080-1234-5678' ],
 [ 'name', '花子', 'age', 24, 'tel', '050-9876-5432' ] 
]
*/
 

文字列をキーとした列挙可能なプロパティの組 [key, value] からなる配列を返します。

説明を読む限り、keyとvalueをセットで返すのが、Object.entriesらしいので、そもそもkeyが不要であれば、このメソッドを使うのは適してませんね。keyが不要な場合は、keyを削除する処理を噛ませる必要があります。

for of

function convertValues() {
 const users = [
   { name:'太郎', age:32, tel:'080-1234-5678' },
   { name:'花子', age:24, tel:'050-9876-5432' },
 ];

 const originalValues = Object.entries(users[0]);
 console.log('originalValues\n\n', originalValues);

 let newArray = [];
 for (const [key, value] of originalValues) {
   newArray.push(value);
 }

 console.log(newArray); //[ '太郎', 32, '080-1234-5678' ]
}

1個だけなら上手くいかない事もない。

function myFunction89(){
 const users = [
   { name:'太郎', age:32, tel:'080-1234-5678' },
   { name:'花子', age:24, tel:'050-9876-5432' },
 ];
 let newValues = [];
 for(const user of users){
   for (const [key, value] of Object.entries(user)) {
   
    //valueとuser[key]は同じ
   
     newValues.push(value);
     console.log(`${key}, ${value}`);
   }
 }
 console.log(newValues);//[ '太郎', 32, '080-1234-5678', '花子', 24, '050-9876-5432' ]
}

ちなみに、下記のようにも変更してみてもダメでした。

newValues.push([value]);

/*
[ [ '太郎' ],
 [ 32 ],
 [ '080-1234-5678' ],
 [ '花子' ],
 [ 24 ],
 [ '050-9876-5432' ] ]
*/

entriesは、使いどころが難しい......。配列変換は厳しい。

結論

導き出した結論としては、基本的には、keysとmapを使うのがベスト。
取得した連想配列から不要なデータを取り除く、SQLでいうところのSELECTみたいな事をしたい場合は、reduceを使うのがベターって感じでしょうか。

Objects.entries()を使用して、2次元配列からオブジェクトを作成する

function createObjects() {
 const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
 const sheet       = spreadsheet.getSheetByName('ハッシュタグ');
 const values      = sheet.getDataRange().getValues();
 const headers     = values.shift();

 console.log(headers);

 const objects     = [];
 let count         = 1;

 for (const row of values) {

   count += 1;
   const object = {};
   
   for (const [index, value] of headers.entries()) {
     //ヘッダー、見出し行の構成要素が、keyになる
     object[value] = row[index];

     console.log(`${count}行目 values[${count -1}][${index}]  ${row[index]}`);
     console.log(`index, ${index}, value, object['${value}']`);
   }
   objects.push(object);
 }

 console.log(objects);
 return objects;
 
}
スクリーンショット 2021-03-07 9.53.48

拾い物ですが、これも、めちゃくちゃ頭が良い!!見出し行の構成要素が、keyになったオブジェクトが一瞬で作成できます。上記の例だと、日付、ハッシュタグ、記事数がkeyになります。

スクリーンショット 2021-03-07 10.06.56
スクリーンショット 2021-03-07 10.02.30
console.log(objects[0]['ハッシュタグ']); //#データマイニング


この記事が参加している募集

#最近の学び

181,238件

この記事が気に入ったらサポートをしてみませんか?