1
1

[GAS, JavaScript] 脱初心者!配列の filter, map, find を使えるようになろう

Posted at

これは何?

非エンジニア向けにGoogleAppsScript(GAS)レクチャーを行なっています。プログラムが全くの初めての人を対象にしています。このレクチャーを卒業し、基本的なことが理解できるようになった人が「その次に知っておいたほうがいいこと」の一つに 配列の扱い があるなと思いました。これを説明してみよう、という記事。

実務でよく使いそうな、filter, map, find についてです。

対象者

[1] 配列の要素のうち特定の条件を満たすものだけを含む配列を作りたい (filter)

与えられた配列のうち、50以上の要素だけを抜き出したい(50以上の要素だけを持つ、新しい配列を生成したい)とします。

まずはこういう書き方になりそう

ケーススタディでしっかり身につく! Google Apps Script超入門に書かれている知識だけで処理しようとすると下記のようになります。

function filter1() {
  // この配列の中から「50以上」の要素だけを取り出して新しい配列にしたい
  const numbers = [80, 40, 80, 30, 60];

  // 結果をいれるカラの配列を作って
  const result = [];

  // for文の中で条件に当てはまる要素を result に push する
  for (const number of numbers) {
    if (50 <= number) {
      result.push(number);
    }
  }

  // result は条件に当てはまるものだけが入った配列になる
  console.log(result);
}
[ 80, 80, 60 ]

filterを使うとこうなる

function filter2() {
  const numbers = [80, 40, 80, 30, 60];
  const result = numbers.filter(number => 50 <= number);
  console.log(result);
}
[ 80, 80, 60 ]

どうでしょうか。コード量がグっと減りましたね。

ただ、初めてこれを見たときに「...ん?」ってなりませんか?私はなりました。
numbers.filter(number => 50 <= number) ← ここね。

=> は「アロー関数」という書き方になりますが、ここがわかってない人は 私がアロー関数を理解するまで を読んでほしい!

  const result = numbers.filter(number => 50 <= number);

についてちょっと無理やり説明すると

numbers.filter(number => 

の部分で

  • numbers配列に対して filterをかけるよ、っていうのがわかる
  • numbers配列から順番に1つずつ要素を取り出して(←ここにループの要素が含まれている)numberという変数に代入するよ

という理解をしています。

さらに、「フィルタリングする条件」は何かというと

50 <= number

であり、この条件を満たした要素をresult配列に入れていく(resultpush)

その結果として「フィルタリングの条件を満たす要素だけが入ったresult配列が生成される」になります。

  const result = numbers.filter(number => 50 <= number);

仕様として

  • filterの戻り値は「配列」です
  • この例だと result[ 80, 80, 60 ]という配列になります
  • フィルタリングの条件を満たす要素が1つもない場合は、空の配列 []になります
  • 元の配列(numbers)は変更されません

詳細について知りたい人はこちら→ Array.prototype.filter()

[2] 配列の要素全てに対して何かの処理をして、新たな配列を作りたい (map)

例えば5人に100点満点のテストを受けてもらって、その結果があったとして、何らかの理由で「全員に+10点する」みたいな処理をしたいとします。

まずはforで書くよね

function mapFunc1() {
  const scores = [80, 40, 80, 30, 60];

  // 結果をいれるカラの配列を作っておいて、
  const result = [];

  // ループさせて各要素に対して+10の処理をして、result に push
  for(const score of scores){
    result.push(score+10);
  }

  // 結果として各要素が+10された配列ができあがる
  console.log(result);
}
実行結果
[ 90, 50, 90, 40, 70 ]

mapを使うとこうなる

function mapFunc2() {
  const scores = [80, 40, 80, 30, 60];

  const result = scores.map(score => score + 10);
  console.log(result); // [ 90, 50, 90, 40, 70 ]
}
const result = scores.map(score => score + 10);

についての理解として

  • scores配列に対してmapの処理をするよ
  • scores配列から1つ取り出した要素をscore変数にいれるよ
  • score + 10 した結果を result に入れていく (resultpush) よ

mapは配列の「全て」の要素に対して、同じ処理をしたいときに使えます。

仕様として

  • mapの戻り値は配列です
  • 元の配列(scores)は変更されません

詳細について知りたい人はこちら→ Array.prototype.map() - JavaScript | MDN

mapの実践編

せっかくなので実務で使いそうなケースを考えてみます。
スプレッドシートでこんなデータがあるとします。

image.png

全ての人に対して「姓+名」の文字列を生成したい、みたいなケースってありそうですよね。

function mapFunc3() {
  // スプレッドシートから取得したデータは2次元配列になっている
  const data = [
    ['田中', '太郎'],
    ['山本', '花子'],
    ['鈴木', '一郎'],
    ['高橋', '由美'],
    ['佐藤', '健太'],
    ['伊藤', '美咲'],
    ['渡辺', '大輔'],
    ['中村', '結衣'],
    ['小林', '拓海'],
    ['加藤', '杏奈']
  ];

  const result = data.map(row => row[0] + row[1]); // 文字列連結の'+'
  console.log(result);
}
実行結果
['田中太郎', '山本花子', '鈴木一郎', '高橋由美', '佐藤健太', '伊藤美咲', '渡辺大輔', '中村結', '小林拓海', '加藤杏奈']

どうでしょう?コードの中身は理解できますか?

今回はdataが二次元配列なので

data.map(row => row[0] + row[1])

は「dataから1つの要素を取り出してrow変数にいれる」わけですが、このときのrow[姓, 名] という配列です。

[3] 配列の要素のうち、条件を満たす要素を1つ取り出す (find)

filterと似ていますが「条件を満たす要素があったらその要素を取り出す」という処理です。

例えばあるクラスでテストを実施して、「名前」と「点数」のリストが存在する。
この中から特定の名前の人の情報を探したい。みたいな。

まずはこういう書き方になるかな。

function findFunc1() {
  const data = [
    { name: '田中太郎', score: 80 },
    { name: '山本花子', score: 40 },
    { name: '鈴木一郎', score: 80 },
    { name: '高橋由美', score: 30 },
    { name: '佐藤健太', score: 60 }
  ];

  const targetName = "鈴木一郎"; // この名前の人の情報を探したい

  // 結果を入れる変数
  let result;

  // forを回して一致する要素があれば result に入れて break する
  for(const member of data){
    if(member.name === targetName){
      result = member;
      break;
    }
  }

  console.log(result);
}
{ name: '鈴木一郎', score: 80 }

findを使って書いてみる

function findFunc2() {
  const data = [
    { name: '田中太郎', score: 80 },
    { name: '山本花子', score: 40 },
    { name: '鈴木一郎', score: 80 },
    { name: '高橋由美', score: 30 },
    { name: '佐藤健太', score: 60 }
  ];

  const targetName = "鈴木一郎"; // この名前の人の情報を探したい

  const result = data.find(member => member.name === targetName);
  console.log(result); // { name: '鈴木一郎', score: 80 }
}
  const result = data.find(member => member.name === targetName);
  • dataに対してfindを実行するよ
  • dataから1つ取り出してmember変数にいれるよ
  • member.name === targetName を満たす要素があったら result に代入するよ

という意味です。

仕様として

  • 条件を満たす要素が存在しない場合は undefind を返します
  • 条件を満たす要素が複数ある場合、条件を満たす最初の要素を返します

になります。filterは条件を満たす要素すべてを配列として返しますが、findは「最初に見つかった要素だけ」を返します。

詳細について知りたい人はこちら→Array.prototype.find() - JavaScript | MDN

おわりに

JavaScript、GASでは「配列を扱う便利な関数」がいっぱいあります。

Array - JavaScript | MDNで調べてみてください。

今回紹介した3つ以外では concat, every, flat, includes, join, some などがよく使いそうでしょうか。

配列を操作するときには「自分で処理を考えるより先に、それをやってくれる関数を探す」のがいいと思います!

1
1
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
1
1