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

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

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

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

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

5368閲覧

[JavaScript]連想配列の中に配列を入れたい[GAS]

Yohei04

総合スコア24

Google Apps Script

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

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2019/07/16 01:48

編集2019/07/16 03:03

前提・実現したいこと

GASで取得した2列の日付を連想配列のキーにして名前を値として出力したいです。
例えば下記表で言うと

日付名前
2019-07-01安田たかし
2019-07-02橋本すぐる
2019-07-02佐々木こうじ
2019-07-03河本あいり
2019-07-03猪野しょうへい
2019-07-03寺島ゆうこ
2019-07-03寺島ゆうこ
2019-07-03寺島ゆうこ
2019-07-04石田しょうご
2019-07-04渡辺げんき
2019-07-05安藤けい
2019-07-06安藤けい
2019-07-07安藤けい
2019-07-07渡辺げんき

↓このようにログ出力し、日付ごとの名前の数を数えたいです。

var info = {
2019-07-01 : ['安田たかし'],
2019-07-02 : ['橋本すぐる', '佐々木こうじ'],
2019-07-03 : ['河本あいり', '猪野しょうへい', '寺島ゆうこ'],
2019-07-04 : ['石田しょうご', '渡辺げんき'],
2019-07-05 : ['安藤けい'],
2019-07-07 : ['渡辺げんき'],
}

注意点としては2019-07-03の寺島ゆうこのように同じキーで同じ値の重複は数えないようにしたいです(連想配列の場合そもそも数えられないですか?)
また安藤けいのように前日にもある名前は数えないようにしたいです。
ただ渡辺げんきのように日付が続いてなければ数えたいです。

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

同一キーに対して複数の値を入れる方法がわからない。

該当のソースコード

GAS

1 2 3function test(){ 4// シート取得 5 var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); 6 var data = sheet.getDataRange().getValues(); 7 8 for(var i = 1; i <= lastRow-1; i++){ 9 var obj = {}; 10 11// 日付と名前取得 12 var CI = Moment.moment(data[i][9]).format('YYYY-MM-DD') 13 var name = data[i][4] 14// 連想配列の作成 15 obj[CI] = name 16 17 Logger.log(obj) 18 } 19}

試したこと

上記コードのように、中に配列のない複数の連想配列を作ることはできました。
もしくはforの外にvar obj = {};を出して一つの連想配列は作れたのですが、キーが重複しているため一つのキーに対して値が一つのものしか作れませんでした。

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

初心者のためコードに簡単なコメントアウトを頂けると幸いです。
よろしくお願い致します。

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

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

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

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

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

guest

回答2

0

ベストアンサー

1つだと文字列で2つ以上は配列という管理の仕方は最悪なのでやめましょう
1つでも要素一つの配列で管理してください

sample

元データの持ち方次第ですが、こんなかんじ?

javascript

1var a=[ 2 ["2019-07-01","安田たかし"], 3 ["2019-07-02","橋本すぐる"], 4 ["2019-07-02","佐々木こうじ"], 5 ["2019-07-03","河本あいり"], 6 ["2019-07-03","猪野しょうへい"], 7 ["2019-07-03","寺島ゆうこ"], 8 ["2019-07-03","寺島ゆうこ"], 9 ["2019-07-03","寺島ゆうこ"], 10 ["2019-07-04","石田しょうご"], 11 ["2019-07-04","渡辺げんき"], 12 ["2019-07-05","安藤けい"], 13 ["2019-07-06","安藤けい"], 14 ["2019-07-07","安藤けい"], 15 ["2019-07-07","渡辺げんき"], 16 ]; 17var b={}; 18a.forEach(x=>{ 19 if(!b[x[0]]) b[x[0]]=[]; 20 if(b[x[0]].indexOf(x[1])<0) b[x[0]].push(x[1]); 21}); 22console.log(b);

課題

2019-07-05 安藤けい
2019-07-06 安藤けい
2019-07-07 安藤けい

7/6のデータは7/5にあるから消すとして、7/7のデータは7/6からすでに取り除かれていいても
7/6にあるという判断で抜くのですよね?
また、前日とかぶることで値が消え要素数が0になった7/6は、まるっと削除ですよね?

javascript

1var a=[ 2 ["2019-07-01","安田たかし"], 3 ["2019-07-02","橋本すぐる"], 4 ["2019-07-02","佐々木こうじ"], 5 ["2019-07-03","河本あいり"], 6 ["2019-07-03","猪野しょうへい"], 7 ["2019-07-03","寺島ゆうこ"], 8 ["2019-07-03","寺島ゆうこ"], 9 ["2019-07-03","寺島ゆうこ"], 10 ["2019-07-04","石田しょうご"], 11 ["2019-07-04","渡辺げんき"], 12 ["2019-07-05","安藤けい"], 13 ["2019-07-06","安藤けい"], 14 ["2019-07-07","安藤けい"], 15 ["2019-07-07","渡辺げんき"], 16 ]; 17var b={}; 18a.forEach(x=>{ 19 if(!b[x[0]]) b[x[0]]=[]; 20 if(b[x[0]].indexOf(x[1])<0) b[x[0]].push(x[1]); 21}); 22var c={}; 23for(var i in b){ 24 var d=new Date(i); 25 d.setDate(d.getDate()-1); 26 var pre=(d.toLocaleDateString('en-US',{year:"numeric"}) 27 +"/"+d.toLocaleDateString('en-US',{month:"2-digit",day:"2-digit"})).replace(///g,"-"); 28 c[i]=!b[pre]?b[i]:b[i].filter(x=>x.indexOf(b[pre])<0); 29 if(c[i].length==0) delete(c[i]); 30}; 31console.log(c);

投稿2019/07/16 02:14

編集2019/07/16 03:34
yambejp

総合スコア114839

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

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

Yohei04

2019/07/16 02:25

ご返信ありがとうございます。 つまり連想配列の中に配列を入れるのは良くない構造ということでしょうか? 自分で書いたコードのように重複関係なく日付と名前で一組ずつ連想配列にした方が管理しやすいということでしょうか? 完全に独学のため正直どうするのがベストなのかもわからない状態で、すみません…。
yambejp

2019/07/16 02:46

いえ、例えば 2019-07-01 : '安田たかし', ↓↓↓ 2019-07-01 : ['安田たかし'], とすべきという、意図です データ形式が確定しないと落とし込み処理が始められないので あえて最初にご指摘しました。
Yohei04

2019/07/16 02:58

ご返信ありがとうございます。 あ、あるほど。 確かにプログラミング上では[]のあるなしで全然違いますね…! ご指摘大変ありがとうございます。 以後気を付けます!
yambejp

2019/07/16 03:11

sample上げておきました
Yohei04

2019/07/16 04:16

ご回答ありがとうございます。 7/6のデータは7/5にあるから消すとして、7/7のデータは7/6からすでに取り除かれていいても 7/6にあるという判断で抜くのですよね? →その通りです。 また、前日とかぶることで値が消え要素数が0になった7/6は、まるっと削除ですよね? →その通りです。 GASで実装しているのでアロー関数とかを書き換えて試してみます。 ありがとうございました!
guest

0

JavaScript

1// 未テスト 2function test(){ 3 // シート取得 4 var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); 5 var data = sheet.getDataRange().getValues(); 6 var obj = {}; 7 for ( var i = 1; i <= lastRow-1; i++ ) { 8 // 日付と名前取得 9 var CI = Moment.moment( data[ i ][ 9 ] ).format( 'YYYY-MM-DD' ); 10 var name = data[ i ][ 4 ]; 11 if ( obj[ CI ] && typeOf obj[ CI ] === 'string' ) { 12 obj[ CI ] = [ obj[ CI ], name ]; 13 } else if ( obj[ CI ] ) { 14 obj[ CI ].push( name ); 15 } else { 16 obj[ CI ] = name; 17 } 18 } 19 Logger.log(obj) 20}

【typeof - JavaScript | MDN】
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/typeof

【Array.prototype.push() - JavaScript | MDN】
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/push

投稿2019/07/16 02:03

kei344

総合スコア69407

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

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

Yohei04

2019/07/16 02:16

迅速なご回答ありがとうございます。 早速実行してみたのですが、 if ( obj[ CI ] && typeOf obj[ CI ] === 'string' ) { の行に 条件の後に ) がありません。 (line 12, file "コード") とエラーが出てしまいます。 ()はちゃんとあるように思うのですが。。
kei344

2019/07/16 02:20

if ( obj[ CI ] && typeOf obj[ CI ] === 'string' ) { ↓こう変えても出ますか? if ( obj[ CI ] && ( typeOf obj[ CI ] === 'string' ) ) { 最近GASをさわっていないので、動かない書き方をしているかもしれません。
Yohei04

2019/07/16 02:28

書き換えましたが 挿入句に ) がありません。 (line 12, file "コード") と出てしまいました。
Yohei04

2019/07/16 02:35

typeOf →typeof に換えたら動きました。 失礼しました。
kei344

2019/07/16 02:42

あ、すみません。typeof でしたね。
Yohei04

2019/07/16 02:51

ただ少しおかしいのが、出力した日付が連続したものではなく 2019-07-03 2019-07-07 2019-07-05 2019-07-02 のように順番がかなりばらばらなのですがこれは気にしなくても大丈夫ですか? Logger.log(CI)するとちゃんと日付順です。 またlastRow-1を31などに換えてもちゃんと日付順になります。 参考コードにはないですが var lastRow = sheet.getLastRow(); の形で取得しています。
kei344

2019/07/16 02:56

JavaScriptのオブジェクトは順序を保持しません。順序も考えつつ使用したいなら配列にオブジェクトを入れる形にするほかありません。 ↓(「配列にオブジェクトを入れる形」のイメージ) var array = []; for ( var i = 1; i <= lastRow-1; i++ ) { var obj = {}; array.push( obj ); }
Yohei04

2019/07/16 03:15

JavaScriptのオブジェクトは順序を保持しません。 →そうなんですね。。 確かに「lastRow-1を31などに換えてもちゃんと日付順になります。」と書きましたがよく見たら確かにこっちも順番バラバラでした。失礼いたしました。 また上の方にご指摘頂き気づいたのですが、最初 2019-07-01 : '安田たかし', と書いていたのですが ↓↓↓ 2019-07-01 : ['安田たかし'], の間違いです。 ”実現したい事”も更新しました。 if ( obj[ CI ] && typeOf obj[ CI ] === 'string' ) { であえて文字列にして頂いたのに申し訳ありません。
Yohei04

2019/07/17 02:53

申し訳ありません、質問なのですが、 if ( obj[ CI ] )がtrueというのはどういうことなのかわからず昨日からずっと悩んでいます。 Logger.log(obj)で細かく出力した感じ最初に } else { obj[ CI ] = name; の部分が実行されて次に obj[ CI ].push( name ); が実行されているというのはわかったのですが、キーがtrue??という状態です。 また実現したい事に書いていました、 「注意点としては2019-07-03の寺島ゆうこのように同じキーで同じ値の重複は数えないようにしたいです(連想配列の場合そもそも数えられないですか?) また安藤けいのように前日にもある名前は数えないようにしたいです。 ただ渡辺げんきのように日付が続いてなければ数えたいです。」の 「同じキーで同じ値の重複は数えないように」は for (var key in obj) { var b = obj[key].filter(function (x, i, self) { return self.indexOf(x) === i; }); でできたのですが、 「前日にもある名前は数えないように」と「日付が続いてなければ数えたい」のところが、 上記のコードで全キーにはアクセスできたので、あとは前後の日付の名前で重複を見つければよいと思うのですがそのやり方がわらず。。 お時間のある時で構いませんのでご教授頂けないでしょうか?
kei344

2019/07/17 03:25

> キーがtrue?? キーってどの部分をさしておられますか? その部分は obj に CI('2019-07-03'とかの文字列)という要素があるかを確認しているだけです。 提示のコードの場合はその前の部分で obj[ CI ] が文字列で無いことがわかっているので、配列として扱って(.pushを使って)います。 初期質問時と仕様が変わったので、現在の仕様にはyambejpさんの書かれたコードを基に進められたほうが早いと思いますよ。
Yohei04

2019/07/17 04:05

>キーってどの部分をさしておられますか? obj[ CI ]がobjのキー(日付)になっているのだと思っていました。 それが} else { obj[ CI ] = name;の代入?でキー(日付)と値(名前)のペアでオブジェクトが作られて行っているものだと思っていました。 もう少しオブジェクトについて理解を深めようと思います。 >初期質問時と仕様が変わったので 注意点としては…のところは初期質問時から記載のあった部分になります。 >yambejpさんの書かれたコードを基に進められたほうが早い yambejpさんのコードは自分にはレベルが高く難しいというのが正直なところです。 またkei344さんのコードは自分が最初に書いていたコードを元に作って頂き自分でも理解できそうだなと思いkei344さんに再度ご質問致しました。 ただ、terateilでは回答者様の時間だけを取りこちらからは何も与えられず、流石にこれ以上は申し訳ないのでここからはyambejpさんのコードをヒントにもう少し自分で考えてみようと思います。 不快な思いをしていたら申し訳ありません。。
kei344

2019/07/17 04:41

>>初期質問時と仕様が変わったので >注意点としては…のところは初期質問時から記載のあった部分になります。 いえ、持つ値が全て配列になった部分です。 特に不快というわけではありませんが、問題なく仕様に沿っているコードがあるので良いかと思いました。 > あとは前後の日付の名前で重複を見つければよいと思うのですがそのやり方がわらず。。 オブジェクトは並び順が保障されていないので、こういう処理の時に面倒です。オブジェクトを入れた配列なら、forで一つずつ値を確認しながら前日の値も確認して、という処理が可能です。 まあ現在提示しているコードに前日分の人名配列を保存する変数をつければ出来るとは思います。(連続数日分とか考えると面倒ですが)
Yohei04

2019/07/17 07:58 編集

ご返信ありがとうございます。 >いえ、持つ値が全て配列になった部分です。 そこの部分でしたか。大変失礼致しました。 >現在提示しているコードに前日分の人名配列を保存する変数をつければ出来るとは思います。 わざわざヒントまで頂きありがとうございます。 今思いつきましたが名前の数がわかればいいので、 2019-07-05 : ['安藤けい'] 2019-07-06 : ['安藤けい'] とあったら6日の値を5日にプッシュして被った数を6日から引くのでも良いかもしれません。 文字列'2019-07-05'を使えば値を取り出せたのですが変数CIを使ったときの値の取り出しに苦戦しています。 あとはググりながら自力でやってみます! ここまでご丁寧にありがとうございました。 お手数おかけしました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問