質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

1回答

2729閲覧

【GAS】配列から空白値を除外する方法

donguriko

総合スコア30

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

1クリップ

投稿2022/11/17 12:54

前提

スプレッドシートの値を配列で取得したいと考えています。
データ取得元のシートが行方向、列方向にセル結合されていて、
4セルが1つとして結合されている書式です。

(イメージ)
イメージ説明

実現したいこと

セル結合がされたままだと、うまくデータ取得ができないので、
getRange().breakApart() でセル結合を解除したうえで、
一旦、空白部も含めて配列としてデータ取得し、取得後に空白部を
除外したいです。

発生している問題・エラーメッセージ

①どのようにコードを修正すればよいかアドバイスをいただけない
でしょうか?

以下の2つのやり方を考えましたが、いずれもコードがうまく
書けず、思った結果が得られません。

(案1)  /* */部のコード
値が入ってくるのは、行インデックス部が偶数の場合のみ。
spliceで行インデックスが偶数の時に値を取得し、新しい配列arr1に
pushで値追加すればよいのではないか?

(案2)
filter() で !==" " で抽出 すればよいのではないか?
値が絞れない。

③もし、アロー関数を使用とどのようなコードになりますか?
類似の質問 (Q328508) を検索すると
=> アロー関数? を使用する方法もあることが分かったのですが、
知識不足でGAS本を読んでもアロー関数の意味や使い方が
よく分かりませんでした。 
回答は急ぎませんので、今回のケースでビギナーでも理解できる
レベルで解説をいただけないでしょうか?

④案1、2、アロー関数を使用する方法だとどれがおすすめですか?
今後のためにその理由も教えていただけないでしょうか?

該当のソースコード

最終的にはA、B、、、、とまとめて取得しようと考えている
のですが、まずは一番シンプルなAの部分だけに限定して試しで
コードを書きました。

GAS

1function merge() { 2const ss = SpreadsheetApp.getActiveSpreadsheet(); 3const Sheet = ss.getSheetByName('シート1'); 4 5//セルD4:D22まで配列で取得 6const myRange = Sheet.getRange(4,4,19,1).getValues(); 7console.log("myRange " +myRange); 8const arr0 =[myRange]; 9console.log ("arr0 " +myRange); 10 11const trimarr = myRange.filter(function(item){ 12 return item !== "";  //絞れない。。 13}); 14console.log("trimarr " +trimarr); 15 16/* うまくできない。。。 17const arr1 =[]; 18for (let i = 0; i < 20; i++ ){ 19 if(i%2===0){ 20 const score = arr0.splice(i,1); 21 console.log("score " + score); 22 arr1.push(score); 23 console.log("arr1 " + arr1); 24 } 25} 26*/ 27} 28

補足情報(FW/ツールのバージョンなど)

次回から自力でfilter() や、for文と組み合わせたspliceを使える
ようになりたいです。
回答は急ぎませんので、非エンジニアビギナーでも理解できる
レベルの解説、アドバイスをいただけると助かります。
よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

これでどうでしょう?

javascript

1function merge() { 2 const ss = SpreadsheetApp.getActiveSpreadsheet(); 3 const sheet = ss.getSheetByName('シート1'); 4 5 const values = 6 sheet.getRange(4,4,19,3) 7 .getValues() 8 .filter(([v]) => v) 9 .map(row => row.filter(v => v)); 10 11 console.log(values); 12}

上記によって values は以下のような配列になるのではと想定しています。

[ [ 1, 11 ], [ 2, 12 ], [ 3, 13 ], [ 4, 14 ], [ 5, 15 ], [ 6, 16 ], [ 7, 17 ], [ 8, 18 ], [ 9, 19 ], [ 10, 20 ] ]

追記

コメントに返信します。

前提

a), b) に答える前にまず、.filter(([v]) => v).map(row => row.filter(v => v)) の処理をやらないで、getValues() するところまでにして

javascript

1const values = sheet.getRange(4,4,19,3).getValues();

としたとすると、values には以下のような配列が入ってきます。

[ [ 1, '', 11 ], [ '', '', '' ], [ 2, '', 12 ], [ '', '', '' ], [ 3, '', 13 ], [ '', '', '' ], [ 4, '', 14 ], [ '', '', '' ], [ 5, '', 15 ], [ '', '', '' ], [ 6, '', 16 ], [ '', '', '' ], [ 7, '', 17 ], [ '', '', '' ], [ 8, '', 18 ], [ '', '', '' ], [ 9, '', 19 ], [ '', '', '' ], [ 10, '', 20 ] ]

すなわち、values は以下のようなものになっています。
(1) 全体が配列であり、
(2) その要素もまた配列であり、
(3) 要素の配列は長さが3で、次の2とおりの場合がある。

  • [整数, '', 整数] (ただし整数は1以上)
  • ['', '', '']

上記のような配列に対して以下の2つの処理[A], [B] を行います。

[A] .filter(([v]) => v) によって要素 ['', '', ''] を取り除いた、以下のような配列を得る。

[ [ 1, '', 11 ], [ 2, '', 12 ], [ 3, '', 13 ], [ 4, '', 14 ], [ 5, '', 15 ], [ 6, '', 16 ], [ 7, '', 17 ], [ 8, '', 18 ], [ 9, '', 19 ], [ 10, '', 20 ] ]

[B] 処理[A]の結果得られた上記の配列の各要素である配列 [整数, '', 整数] から空文字列を除いた [整数, 整数] を各要素とする配列

[ [ 1, 11 ], [ 2, 12 ], [ 3, 13 ], [ 4, 14 ], [ 5, 15 ], [ 6, 16 ], [ 7, 17 ], [ 8, 18 ], [ 9, 19 ], [ 10, 20 ] ]

を得る。

上記の2つの処理[A], [B]のうち [A]を行っているのが .filter(([v]) => v) で、[B]を行っているのが .map(row => row.filter(v => v)) です。

以上をふまえて質問に回答します。

質問 a

a) .filter(([v]) => v) 
ここでフィルタの条件を指定しているんだろうなと推測しているのですが、
([v]) => v は何を意味しているのかが分かりません。

filterメソッドには引数として関数を渡します。([v]) => v もまた関数です。これはアロー関数であり、かつ引数部分に分割代入を使っています。この関数をアローと分割代入を使わないで書くと以下のようになります。

javascript

1function (ary) { 2 return ary[0]; 3}

すなわち、「配列を引数に取り、その配列の先頭要素を返す関数」です。これをアローと分割代入を使えば

javascript

1([v]) => v

とコードを詰めて書けます。

なぜ、このような「配列を引数に取り、その配列の先頭要素を返す関数」を filter に渡せば、今回の質問において処理[A] を行うfilter条件として意図通りの結果になるのか?が分からない場合はまたご質問ください。(ちなみに この関数が先の[A] の処理を行うfilter条件として適切であることは、D列の数字が1以上であることを前提にしています。もしD列の数字が0始まりだとうまくいきません。これについては後述の 補足1 も併せて確認するとよいでしょう)

質問 b

b) .map(row => row.filter(v => v));
上記a)と同様、row => row.filter(v => v)部が何を意味しているのかが
わかりません。

 
.map(row => row.filter(v => v)) では先に書いた処理[B] を行います。つまり、配列

[ [ 1, '', 11 ], [ 2, '', 12 ], [ 3, '', 13 ], [ 4, '', 14 ], [ 5, '', 15 ], [ 6, '', 16 ], [ 7, '', 17 ], [ 8, '', 18 ], [ 9, '', 19 ], [ 10, '', 20 ] ]

から

[ [ 1, 11 ], [ 2, 12 ], [ 3, 13 ], [ 4, 14 ], [ 5, 15 ], [ 6, 16 ], [ 7, 17 ], [ 8, 18 ], [ 9, 19 ], [ 10, 20 ] ]

を得る処理です。この処理では処理前と処理後の(外側の)配列の要素数はともに10個で変わらないです。外側の配列の各要素である内側の配列(例 [1, '', 11])に対して「要素として含まれる空文字列を除去する」という処理を行った配列(例 [1, 11])を新たな要素とする配列を得る、という処理を行います。

このような、「ある配列の各要素に何らかの処理を行った結果の値を要素とする配列を得たい」という場合に使うのは mapメソッドです。

mapメソッドには「各要素を何か別のものにする関数」を引数として渡しますが、今回の場合は各要素もまた配列で、処理として「(要素である長さ3の)配列から空文字列を除いた配列を返す関数」をmapメソッドに渡せばよいことになります。そのような関数をアローを使わないで書くと以下のようになります。今回はスプレッドシートが元データなので引数の名前を row とします。

javascript

1function (row) { 2 const filteredAry = row.filter( 3 function(v) { 4 return v; 5 } 6 ); 7 8 return filteredAry; 9}

上記の関数を、アローを使いかつ中間の変数 filteredAry を無くすことで returnを省略できて、回答のコードにある

javascript

1row => row.filter(v => v)

と書けます。これを map に渡すことで、配列

[ [ 1, '', 11 ], [ 2, '', 12 ], [ 3, '', 13 ], [ 4, '', 14 ], [ 5, '', 15 ], [ 6, '', 16 ], [ 7, '', 17 ], [ 8, '', 18 ], [ 9, '', 19 ], [ 10, '', 20 ] ]

から最終目的の配列

[ [ 1, 11 ], [ 2, 12 ], [ 3, 13 ], [ 4, 14 ], [ 5, 15 ], [ 6, 16 ], [ 7, 17 ], [ 8, 18 ], [ 9, 19 ], [ 10, 20 ] ]

が得られます。

補足1

先述したとおり、

javascript

1row => row.filter(v => v)

という関数に引数として配列を渡すと、その配列に要素として含まれる空文字列が除去された配列が得られますが、除去の対象になるのは空文字列だけではありません。空文字列のほかに何が除去されるか調べて確認してみるとより理解が深まるかもしれません。

補足2

すでに回答したコードでは 処理[B] を行うために mapメソッドの引数に

javascript

1row => row.filter(v => v)

という関数を渡しましたが、引数のrow には [ 1, '', 11 ] といった長さが3の配列が渡されてきて、この配列の必ず2番目の要素である空文字列を除いた配列を返せばよいということが分かっているので、filter を使わずに 処理[A] のコードでも使った、関数の引数の分割代入による受け取りを使った以下の関数

javascript

1([x, _, y]) => [x, y]

をmapの引数に渡しても [B] の処理として意図した結果を得られます。

投稿2022/11/17 13:51

編集2022/11/17 23:01
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

donguriko

2022/11/17 14:45

su507さま お忙しい中早々の回答ありがとうございます。 ご教示いただいたコードでやりたかった処理ができました。 が、当方の知識不足で以下の部分で何を行っているのかの理解が追い付かず。。。 次回から自分で解決できるようになりたいので、もう少し補足説明をいただけない でしょうか? a) .filter(([v]) => v)  ここでフィルタの条件を指定しているんだろうなと推測しているのですが、 ([v]) => v は何を意味しているのかが分かりません。   [v]はフィルタ絞込みをした値を入れた新しい配列ですか?   a)の部分のコードについてもう少し補足説明をいただけないでしょうか? b) .map(row => row.filter(v => v)); 上記a)と同様、row => row.filter(v => v)部が何を意味しているのかが わかりません。     
退会済みユーザー

退会済みユーザー

2022/11/17 23:01

コメントありがとうございます。回答のほうに返信しました。
donguriko

2022/11/20 00:31

su507さま 解説を読み込むの時間を要し、返信が遅くなり申し訳ありません。 丁寧な解説をありがとうございます。 [A][B]で何をやっているのか、理解をすることができました。 が、まだ何点か理解が追い付かない部分があるので「さら問」をさせてください。 ●さら問1 [A]で「配列を引数に取り、その配列の先頭要素を返す関数」を filter に渡せばいいのか、 がよくわかりません。 そもそもの理解が少し怪しいのですが、 ここでいう元の配列の要素とは、内側の配列のことで要素1 [ 1, '', 11 ]や要素2[ '', '', '' ] 要素3 [ 2, '', 12 ] のことという理解であっていますか? で、各要素の先頭要素となると[[1],[" "],[2]・・・・[9]]ということでしょうか? どうして先頭要素を返せばよいのかがわかりません。。。 ●さら問2 function (ary) { return ary[0]; } を、アローと分割代入で ([v]) => v に書き換える部分。 function (ary) を v と書き換え、 ary[0]を[v]と書き換えるという理解であっていますか? 以降の説明で出てくる、function (v)は、上記説明のfunction (ary) のこと という理解でOK? ちなみに今回は(vを使うケースが多いからと思うのですが)vとしているが、 使用する文字はaとかbとかv以外の文字でもよい? ●さら問3 補足1、2説明部 row => row.filter(v => v) は row => row.filter([v]=> v) が正しい?? ★補足2の別解もありがとうございます。参考になりました。 ●さら問4 補足1「除去の対象になるのは空文字列だけではありません。 空文字列のほかに何が除去されるか調べて確認してみる」で、 各行のD列やF列を1→0に置き換えてconsole.logで確認してみました。 値0も除去されるんですね。 ここまでビギナー向けにかみ砕いた解説を記載するのはかなりのお手間 だったかと思います。 親切、丁寧なレクチャー、本当にありがとうございます。感謝しかありません。 お忙しいかと思うので、さら問の回答は急ぎませんので、ご無理のない時で 大丈夫です。
退会済みユーザー

退会済みユーザー

2022/11/21 12:08

> ●さら問1 について回答します。 > [A]で「配列を引数に取り、その配列の先頭要素を返す関数」を filter に渡せばいいのか、 > がよくわかりません。 > そもそもの理解が少し怪しいのですが、 > ここでいう元の配列の要素とは、内側の配列のことで要素1 [ 1, '', 11 ]や要素2[ '', '', '' ] > 要素3 [ 2, '', 12 ] のことという理解であっていますか? あっています。 > で、各要素の先頭要素となると[[1],[" "],[2]・・・・[9]]ということでしょうか? 要素1 [ 1, '', 11 ] の先頭は 1 です。 要素2 [ '', '', '' ] の先頭は '' (空文字列)です。 要素3 [ 2, '', 12 ] の先頭は 2 です。 要素4 [ '', '', '' ] の先頭は '' (空文字列)です。 要素5 [ 3, '', 13 ] の先頭は 3 です。 ・・・ 以下同様 となります。つまり回答のコードに書いた .filter(([v]) => v) の部分の v が取り得る値は 1, '', 2, '', 3 ・・・ となります。 > どうして先頭要素を返せばよいのかがわかりません。。。 処理Aでは [ [ 1, '', 11 ], [ '', '', '' ], [ 2, '', 12 ], [ '', '', '' ], [ 3, '', 13 ] ・・・ ] という配列をフィルターして、 [ '', '', '' ] を除外することで [ [ 1, '', 11 ], [ 2, '', 12 ], [ 3, '', 13 ], ・・・ ] という配列を得たいわけですが、内側の配列の任意のどれかについてそれが残すべきものか除外すべきものかを判定するには、先頭の要素だけを見れば判定できます。というのは 「先頭要素が空文字列でなければ残し、空文字列だったら除外する」--- (ⅰ) という判定をすればよいからです。 ところで先頭要素が取り得る値は先ほどみたように、 「空文字列であるか、もしくは1以上の整数であるかのいずれか。これら以外の値は取り得ない」 ということが分かっているので、上記のフィルター条件(ⅰ)は、先頭要素 が空文字列でないことの判定 ( v !== '' )をするまでもなく 「先頭要素がtruthyだったら残し、そうでなければ除外する」--- (ⅱ) という判定が使えます。この判定(ⅱ)が使えるかというと、対象とする問題固有の前提として「先頭要素がtruthyにならないのは先頭要素が空文字列であるときに限る」からです。そして問題固有の前提によって(ⅰ)ではなく(ⅱ)を使いたい理由はコードが簡単になるからです。 さて新しい言葉を出しました。truthyという用語です。 ある値が 「truthy である」とはその値が真偽値として評価されるときに true に評価される値のことです。truthy の逆は「 falsy 」といい、falsyな値は真偽値として評価されるときに false に評価される値のことです。 すでに確認されていることをfalsyという用語で整理すると、空文字列と整数の0はfalsyな値です。boolean の false もまた(当然のことですが) falsy な値です。falsy な値のすべての一覧は以下を参考にしてください。 https://developer.mozilla.org/ja/docs/Glossary/Falsy falsyな値ではない値はすべてtruthyです。truthy の説明は以下です。 https://developer.mozilla.org/ja/docs/Glossary/Truthy truthyとfalsyの定義を押さえておいて、あらためて filter の仕様の説明を確認しましょう。 https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/filter 説明の中で「返値」の下に「解説」があり、ここに > filter() は、与えられた callbackFn 関数を配列の各要素に対して一度ずつ呼び出し、callbackFn が true に評価される値を返したすべての要素からなる新しい配列を生成します。 とありますがこの説明文の中で以下が重要です。 > callbackFn が true に評価される値を返したすべての要素・・・ 何が重要かというと、ここが callbackFn が true を返したすべての要素・・・ とはなっていなくて、 > true に評価される値・・・ となっていることがキモです。Truthyの説明を確認すれば分かるように、 true (というブール値)は、true に評価される(他のたくさんの)値つまり truthy な値のひとつに過ぎません。 すなわち、filterの引数に与える関数は true か false のいずれかを返さなければならないわけではなく、どんな値を返してもよく、その値が true に評価される値つまり truthy な値を返したときは、引数として渡された要素は残され、 truthy ではない、つまりfalsyな値を返したときは除外されることになります。 Tuthy および Falsy の定義とfilter の仕様のコールバックの「解説」をよく読み、今回の質問の状況での処理[A]を行うためのフィルター条件としてちゃんと書けば (ⅰ)の v !== '' なのだけれど、解決すべき問題固有の前提を使えば (ⅱ)つまり v そのものを返すのでもOKであるということを(願わくば、D列が0始まりだと(ⅱ)は使えない、ということも含めて)納得に至っていただけることを願って止みません 🙏
退会済みユーザー

退会済みユーザー

2022/11/21 12:31

> ●さら問2 について回答します。 ① function (ary) { return ary[0]; }  を、 ([v]) => v  に書き換えるのを段階を追ってやっていきます。まず①にアローを使うと以下の②になります。 ② (ary) => { return ary[0]; } 次に、この関数の本体をみると引数の ary は配列である前提であり、使われるのは ary の先頭のみなので、分割代入を使うことで以下の③になります。 ③ ([v]) => { return v; } 最後に、③の関数本体は return 文しか無いので、アロー関数の本体にreturn 文しかない場合に return を省略した書き方ができることを使って ④ ([v]) => v となります。 質問に答えると > ary[0]を[v]と書き換えるという理解であっていますか? ついてですが、「ary[0]を[v]と書き換える」のではなく上記の②から③のステップを見ると分かるとおり、引数の ary を [v] と書き換えることで、関数本体のary[0] を v と書くことができています。 分割代入をご自身のモノにするには一度は以下をじっくり精読する必要があろうかと思います。 https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment > 以降の説明で出てくる、function (v)は、上記説明のfunction (ary) のこと という理解でOK? その理解は正しくないです。 > 以降の説明で出てくる、function (v) とは質問 b) に対する回答の中に例示したコード function (row) { const filteredAry = row.filter( function(v) { return v; } ); return filteredAry; } に出てくる function(v) のことだと思ってますが、こちらは map に渡す関数の引数なので、filter に渡す関数 のfunction (ary) とは別ものです。 > ちなみに今回は(vを使うケースが多いからと思うのですが)vとしているが、 使用する文字はaとかbとかv以外の文字でもよい? v以外の文字でもよいです。 > ●さら問3 > 補足1、2説明部 > row => row.filter(v => v) は row => row.filter([v]=> v) > が正しい?? そう思うのであれば試しにそのように修正して実行してみるとよいでしょう。 期待している結果が得られるでしょうか?
退会済みユーザー

退会済みユーザー

2022/11/21 12:47

> ●さら問4 補足1「除去の対象になるのは空文字列だけではありません。 空文字列のほかに何が除去されるか調べて確認してみる」で、 各行のD列やF列を1→0に置き換えてconsole.logで確認してみました。 値0も除去されるんですね。 はい。 row.filter(v => v) というコードは 配列.filter(v => v) という形になっていますが、これは先に説明した falsy という用語を使えば、配列から falsy な要素が除去された新たな配列を得る、というものです。たとえば [3, '', 13] という配列に対して [3, '', 13] .filter(v => v) とすると [3, 13] が返されます。 再確認すると、今回の処理[A] で出てきたコードは 配列.filter(([v]) => v) というものでした。これは、filter対象の配列の要素もまた配列であって、この要素である配列(内側の配列)の先頭要素がtruthy である配列だけが残された(外側の)配列を得る、というものです。 最後にひとこと 様々なことを書いてしまったのでかえって混乱させてしまったかもしれませんが健闘をお祈り申し上げます。
donguriko

2022/11/21 13:16

su507さま お忙しい中、ステップを踏んだ丁寧な解説ありがとうございます。 また、truthy、falsy、 filter 仕様のリンクもありがとうございます。 回答の補足1の「調べてみる」時には、リンクを貼っていただいた サイト等で調べるとよいのですね。 現状、バッチリ理解できた、これで次回から自力でコードが書ける、 使えるぞ、というレベルにはほど遠いですが、su507さまの解説で 理屈はなんとなくわかった気がします。 が、1回ではとても内容を消化しきれないので、 (リンク先のような文書を読みなれないこともあり、読むだけで大苦戦) リンク先内容および、回答内容をじっくり読み返んで理解を深めたい と思います。 私のようなビギナーの質問にも丁寧にレクチャーしてくださり本当に 感謝です。貴重な時間をありがとうございました!!  とても勉強になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問