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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

Google Apps Script

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

Q&A

解決済

1回答

640閲覧

【GAS】haystackからNeedleを探す_検索対象が日付の場合

donguriko

総合スコア30

Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

Google Apps Script

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

0グッド

0クリップ

投稿2022/12/12 10:22

前提

右記リンク先質問のつづきです。⇒ 前回質問

発生している問題・

前回レクチャーいただいた内容をもとに
Haystack2リスト、Needles2リストの検索対象を日付に
置き換えました。
イメージ説明

各表内の値が果物・野菜の名前の時は想定どおり、
Result欄に「'●'」フラグを返すことができたのですが、
日付に置き換えたところ、resultがすべてfalse判定と
なってしまいます。。。

Q:検索対象が日付データの場合には、別途 何かひと手間を
かけないと、正しく判定されないのでしょうか?

該当のソースコード

以下にOK(果物の名前)ver、NG(日付)ver双方のコードを
記載します。

GAS

1■OKver************************************************************** 2function searchNeedleInHaystack() { 3 const ss =SpreadsheetApp.getActiveSpreadsheet(); 4 const sheet = SpreadsheetApp.getActiveSheet(); 5 6 const haystack = sheet.getRange(4, 14, 4, 1).getValues(); 7 const haystackf = haystack.flat(); 8 console.log('haystackf ',haystackf); 9 //正解 [ 'apple', 'banana', 'orange', 'kiwi' ] 10 11 const needles = sheet.getRange(4, 16, 3, 1).getValues(); 12 const needlesf = needles.flat(); 13 console.log('needlesf ',needlesf); 14 // [ 'apple', 'tomato', 'banana' ] 15 16 //▼Haystackfの中からNeedleを探す 17 //結果はresult で返ってくる 18 19 const result2 = needlesf.map(needlesf => haystackf.includes(needlesf) ? ['●'] : ['']); 20 21// .map()で新しい配列を生成、結果を新配列resultとして戻す 22// (判定対象)配列needlesf 23// (判定条件)haystackf.includes(needlesf) 24// (trueの処理) ? 以降  配列needlesに ['●']を返す 25// (falseの処理) : 以降  配列needlesに [' ']を返す 26 27 console.log("result2 " +result2); 28 // [ ['●'], [''], ['●'] ] 29 30//▼判定結果をQ列にアウトプットする 31 sheet.getRange(4, 17, 3, 1).setValues(result2); 32 33} 34 35■NGver************************************************************************** 36function searchNeedleInHaystack2() { 37 const ss =SpreadsheetApp.getActiveSpreadsheet(); 38 const sheet = SpreadsheetApp.getActiveSheet(); 39 40 const haystack2 = sheet.getRange(10, 14, 4, 1).getValues(); 41 const haystackf = haystack2.flat(); 42 console.log('haystackf ',haystackf); 43 44 const needles2 = sheet.getRange(10, 16, 3, 1).getValues(); 45 const needlesf = needles2.flat(); 46 console.log('needles2 ',needles2); 47 console.log('needlesf ',needlesf); 48 49 //▼Haystack2の中にNeedles2があるかをチェック 50 //結果はresult で返ってくる 51 52 const result2 = needlesf.map(needlesf => haystackf.includes(needlesf) ? ['●'] : ['']); 53 54// .map()で新しい配列を生成、結果を新配列result2として戻す 55// (判定対象)配列needlesf 56// (判定条件)haystackf.includes(needlesf) 57// (trueの処理) ? 以降  配列result2に ['●']を返す 58// (falseの処理) : 以降  配列result2に [' ']を返す 59 60 console.log('result2 ',result2); 61 // 予想[ ['●'], [''], ['●'] ]  ★うまくできない★ 62 63//▼判定結果をQ列にアウトプットする 64 sheet.getRange(10, 17, 3, 1).setValues(result2); 65} 66

試したこと

OKverのログとNGverのログを見比べてみましたが、
自力では、NGverのダメポイントを見つけられませんでした。

OK(果物・野菜)ver
イメージ説明

NG(日付)ver
イメージ説明

もし原因が単純な見落としでしたら申し訳ありません。

補足情報

お忙しいところ大変申し訳ありませんが、
どこがダメでどのように修正すればよいか、アドバイスを
いただけないでしょうか?

よろしくお願いいたします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

不具合の原因

  • オブジェクトは中身の値が同じでも、別のものとしてとらえるからです。
  • JavaScriptにおいて値には大きく2種類あります。
    • オブジェクト(配列、連想配列、インスタンスなど)
    • プリミティブ値(文字列、数値、真偽値、undefinedなど)
  • プリミティブ型は値が同じものを比較するとtrueとなります。
  • 日付(date型)はオブジェクトですので、たとえ同じ日付でも、比較するとfalseとなります。

javascript

1// プリミティブ値比較の例 2// 数値 3const num1 = 0; 4const num2 = 0; 5console.log('数値:', num1 === num2); // true 6 7// 文字列 8const str1 = 'a'; 9const str2 = 'a'; 10console.log('文字列:', str1 === str2); // true 11 12// オブジェクト比較の例 13// 連想配列 14const obj1 = {x: 100}; 15const obj2 = {x: 100}; 16console.log('オブジェクト:', obj1 === obj2); // false 17 18// date型 19const date1 = new Date(2022,11,11); 20const date2 = new Date(2022,11,11); 21console.log('date型:', date1 === date2); // false

日付の比較方法

2つの日付の差が0のとき、同一日と判断できます。

どういうことかというと、

  • 2022年12月11日 - 2000年10月10日は = 22年2月1日となります。
  • 2022年12月11日 - 2022年12月11日は = 0となります。

ですので、0なら同一日時、(プラスでもマイナスでも)0以外は違う日時ということです。
これをコードで書くと以下のようになります。

javascript

1const date1 = new Date(2022,11,11); 2const date2 = new Date(2022,11,11); 3const result = date1 - date2; 4console.log('同じ日か?:', result === 0); // true

コード例

質問者様のコードを修正するには、2通りの方法があります。

  • 日付(date型)を文字列に変換し、文字列を比較する
  • haystackとneedlesの日付の差が0かどうかで判断する

イメージ説明

date型を文字列に変換する手法

javascript

1function searchNeedleInHaystack2() { 2 const sheet = SpreadsheetApp.getActiveSheet(); 3 4 const haystack = sheet.getRange(2, 1, 4, 1) 5 .getValues() 6 .flatMap(([date]) => Utilities.formatDate(date, 'JST', 'yyyy-MM-dd')); 7 8 const needles = sheet.getRange(2, 3, 3, 1) 9 .getValues() 10 .flatMap(([date]) => Utilities.formatDate(date, 'JST', 'yyyy-MM-dd')); 11 12 const result = needles.map(needle => haystack.includes(needle) ? ['●'] : ['']); 13 14 sheet.getRange(2, 4, 3, 1).setValues(result); 15}

日付の差が0かどうかで判断する手法

javascript

1function searchNeedleInHaystack3() { 2 const sheet = SpreadsheetApp.getActiveSheet(); 3 4 const haystack = sheet.getRange(2, 1, 4, 1).getValues().flat(); 5 const needles = sheet.getRange(2, 3, 3, 1).getValues().flat(); 6 7 const result = needles.map(n => haystack.some(h => h - n === 0) ? ['●'] : ['']); 8 9 sheet.getRange(2, 4, 3, 1).setValues(result); 10}

参考


おまけ:アロー関数を使わずにかく

searchNeedleInHaystack3()関数の以下の1行を、アロー関数を使わずに、かみくだいた書き方で3種類ほどご用意しました。
理解の助けになれば幸いです。

javascript

1const result = needles.map(n => haystack.some(h => h - n === 0) ? ['●'] : ['']);
アロー関数やめてみる

javascript

1const result = needles.map(function(n) { 2 const isExists = haystack.some(function(h) { 3 return h - n === 0; 4 }); 5 6 return isExists === true ? ['●'] : ['']; 7});
三項演算子やめてみる

javascript

1const result = needles.map(function(n) { 2 const isExists = haystack.some(function(h) { 3 return h - n === 0; 4 }); 5 6 if (isExists === true) { 7 return ['●']; 8 } else { 9 return ['']; 10 } 11});
.map()さえやめてみる

javascript

1const result = []; 2for (let n=0; n<needles.length; n++) { 3 let flag; 4 5 for (let h=0; h<haystack.length; h++) { 6 const diff = haystack[h] - needles[n]; 7 if (diff === 0) { 8 flag = true; 9 break; 10 } 11 } 12 13 if (flag === true) { 14 result.push(['●']); 15 } else { 16 result.push(['']); 17 } 18}

投稿2022/12/12 11:43

編集2022/12/12 16:01
Cocode

総合スコア2314

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

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

donguriko

2022/12/12 14:17 編集

Cocodeさま お忙しい中、早々の回答ありがとうございます。 (なじみのない単語だけで、早くもダウンしてしまいそうですが、、) プリミティブはそのまま比較できるが、オブジェクトは見た目が同じでも 別物として判定されるので、そのまま比較できないのですね。 プリミティブ値の文字列に変換したうえでの比較ならOK、理解しました。 目下 「鬼門」の .map() と => ですが、「文字列変換」方式は かろうじてついていけていそうな気がします。 「日付差」方式、理屈は理解できました。 が、 => が2回出てきて 少々混乱気味です。 前回教えていただいたアロー関数、もう1回じっくり読みかえそうと思います。 なので、質問クローズまで少しお時間をいただいてしまいそうです。 申し訳ありません。 a)参考リンクを掲載いただいた、「分割代入」ですが、大変申し訳ありませんが  どこに分割代入が使われているのか、というところから分からないです。。。  消化不良です。どこのことか、教えていただけないでしょうか? b)「文字列変換」方式では、.includes()を使っていますが、  「日付差」方式では、.includes()ではなく.some()を使っています。  .some()と.includes()はどう使い分けるのですか?
Cocode

2022/12/12 15:27 編集

a)どこに分割代入が使われているのか? アロー関数の引数に分割代入を使用しています〜! .flatMap( ([date]) => Utilities.formatDate(date, 'JST', 'yyyy-MM-dd') ); 分割代入を用いない場合は以下のようになります。 .flatMap( arr => Utilities.formatDate(arr[0], 'JST', 'yyyy-MM-dd') ); b).some()と.includes()はどう使い分けるのですか? .includes()の丸括弧の中(引数)は、単純な値(プリミティブ値)しかいれられません。 単純な値が、配列の中に存在するかどうか?を判定したいときにはそれで十分です。 条件式などを書いて、その条件に合致する値が配列の中に存在するかどうか?の判定は、.some()を用います。 .some()の中にはコールバック関数を書きますから、このコールバック関数の中に条件式を書いています。 h - n === 0 ---------- 1つ1つconsole.log()で表示させて、段階的に確認をしていけば理解の助けになるかと思います。 (もうされてそうですが…!)
donguriko

2022/12/19 09:25

Cocodeさま 返信が遅くなり大変申し訳ありません。 「おまけ」の追記ありがとうございます!! アロー関数やめてみる、.map()やめてみる、 ものすごくありがたいです。 まだ、Cocodeさまの記載してくださった解説をたどり、理解するのがやっとで、 自分で アロー関数やめてみる⇒アロー関数使ってみる、 .map() やめてみる⇒ .map() 使ってみる を自分で書いて、行ったりきたりできる レベルにはたどりつけていませんが、自分で書けるように引き続き努力を続けて いきたいと思います。 丁寧な解説、本当にありがとうございます。(感謝) そもそもの理解が追い付いていないからなのですが、 実は .map()で抽出した結果で新しい配列が生成されるのはぼんやり理解できても、 const XX 等明記されていれば、コレだな、と当たりがつけやすいのですが、 アロー関数ありver や  .map() ver だと「1つ1つ途中経過をconsole.log()で 表示させて、段階的に確認」をしようと思っても、console.log()の()の中に何を書けば、 見たいものがみられるのか?  から分からない、、、というジレンマに陥っていました。 これは、解説等を読み込んで場数を踏んでなれていくしかない、でしょうか?
Cocode

2022/12/19 11:47

私が他人のコードを理解しようとする際は、何をしているのかわからない部分をログ表示するようにしています。 つまり、何を示している・しているのかわからない → その部分をconsole.log()に書く。 例) const result = needles.map(n => haystack.some(h => h - n === 0) ? ['●'] : ['']); nが何かわからなければ、 const result = needles.map(n => console.log(n)); と書いてみたらいいですし、 hが何かわからなければ、 const result = needle.map(n => haystack.some(h => console.log(h))); と書く。 書き方が良くなくてエラーが出れば、そのエラーに従って修正する。 > 場数を踏んでなれていくしかない まさにその通りです。 エラーとたくさん出会ってそれを修正していくことで学んでいきます!
donguriko

2022/12/20 09:27 編集

Cocodeさま コメントありがとうございます。 const result = needles.map(n => console.log(n)); 式の中に console.log()ごと入れてしまうのですね! 教えていただいてよかったです。一歩前進です!! 解説本等を見ても、そこまでは書かれていないのでCocodeさまからの アドバイス本当にありがたいです。 nがわからないのでダイレクトにnだけを取り出して console.log('n' ,n)とか、console.log("n "+,n)とか書いて、 確認できない、どうすれば、、と初歩の初歩で躓いていました。 エラーが出た時に、まだエラーの意味やどこがダメなのか、わからないことも 多いですが、みなさんからのアドバイスで、こういう時はこうやって調べればいい とか、調べる時はココのサイトを見に行くといい等 以前よりは少しずつ分かって きた気がします。 丁寧なアドバイスありがとうございました。引き続き精進します!! ありがとうございました。(感謝)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問