実現したいこと
GASで下記の動きをさせたいです。
・特定のドライブに格納された複数のcsvデータを全て繋げてC列の日付順に昇順ソートをかけ、指定のスプレッドシートに転記したい。
前提
・格納するcsvは全て4行目までがタイトル行なので5行目以降(最終行はファイルにより異なる)のデータを転記したい。
・転記先のスプレッドシートは4行目までがタイトル行で、5行目からcsvデータを追加していきたい(上書きNG)
・処理が終わったcsvは処理済みのフォルダを格納するドライブフォルダに格納。
聞きたいこと
初心者なりにネットで調べながらスクリプトを作ってみました(なので下記コードの細かい意味を理解できていない部分もあります)
このスクリプトを動かすと、「Exception: データの列数が範囲の列数と一致しません。データは 10 列ですが、範囲は 7.列です。」というエラーが出ます。
エラーを直しつつ、実現したい動きをさせるにはどのように修正すればよろしいでしょうか?
該当のソースコード
GoogleAppsScript
1function EXCELtoSSAdd() { 2 3 const folder = DriveApp.getFolderById('xxxxxxxxxx');//Excelを格納したフォルダのID 4 const moveDir = DriveApp.getFolderById('xxxxxxxxxx');//使用済みフォルダ(上記フォルダ内) 5 const files = folder.getFiles(); 6 7 let allValues = []; 8 9 while (files.hasNext()) { 10 11 const file = files.next(); 12 const fileId = file.getId(); 13 14 const blob = DriveApp.getFileById(fileId).getBlob(); 15 const csv = blob.getDataAsString(); 16 const values = Utilities.parseCsv(csv); 17 values.shift(); 18 19 allValues = allValues.concat(values);//変数allValuesに各csvデータを追加 20 21 } 22 23 const sheet = SpreadsheetApp.openById('xxxxxxxxxx').getSheetByName('シート名'); 24 sheet.getRange(5, 1, allValues.length, allValues[0].length).setValues(allValues);//5行目A列からデータの最後尾行データの最後尾列にデータをセット 25 26 //シート内でソートしたいセル範囲をgetRangeで指定する 27 let data = sheet.getRange(5, 1, allValues.length, allValues[0].length); 28 //列Cを基準に降順でソートする 29 data.sort({column:3, ascending:true});//(ソートする列番号,降順か昇順か)true昇順、false降順 30 31 const moveFiles = folder.getFiles(); //フォルダ内のファイルを一括取得 32 33 for (let i = 0; moveFiles.hasNext(); i++) { 34 let moveFile = moveFiles.next(); 35 36 moveFile.moveTo(moveDir);//転記済みのファイルを処理済みフォルダに移動 37 38 } 39}
CSVが本来7列なのであれば、
元のCSVファイルのどれかの中に、カンマが含まれる値が入っているものはありませんか?
values.shift();
の後に、
if (values.some(v => v.length > 7)) {
console.log(file.getName());
}
の3行を挿入して実行すると、そのようなファイルがあった場合に実行ログでエラーの前にファイル名が表示されると思います。
それと、
const file = files.next();
const fileId = file.getId();
const blob = DriveApp.getFileById(fileId).getBlob();
のところは、多分元はスプレッドシートをSpreadsheetAppで取得してそれをDriveAppで再取得していたコードの名残だと思いますが、最初からDriveAppでファイルを取得しているので、
const file = files.next();
const blob = file.getBlob();
でよろしいかと思います。
先日に引き続きありがとうございます。
確認したところ、元ファイルにカンマの含まれる列がありました。
転記する過程でcsvのカンマを消すことはできるのでしょうか?
それとも、csvにカンマが含まれている時点でエラーとなって処理が進まないのでしょうか?
例えば、
元のデータが
|A|B,C,D,E|F|G|H|I|J| の7列だったとしても
|A|B,C|D|E,F,G|H|I|J| の7列だったとしても
CSVにしたときに
A, B, C, D, E, F, G, H, I, J
と同じ10列になるので
CSVのうち、どの組み合わせが同じ列だったかは、
CSVだけを見ても判別できないですので、
カンマが含まれている列は元データの特定の列かどうか。
カンマが含まれる列のカンマの数は同じ数なのかどうか。
(例えば金額だと100と1,000と1,000,000ではカンマの数が違う)
カンマは削除するのかそのままで一つの要素にまとめるのか。
など、CSVにする前のデータの具体的な構造がわからないとなんとも言えないです。
この質問で続けるのであれば、
元データの構造などを具体的に記した質問に編集し直すか、
あるいは、
同様に具体的な内容を記して新たな質問としてみることをお勧めします。
通常は、CSVにする段階でカンマを処理すると思いますが、
それは検討できないのでしょうか?
詳しくありがとうございます。
こちらのcsvですが、ウェブ上のサービスでダウンロードできる集計結果でして、今回のツールを使用する方としては、手動で加工することなくダウンロードしてきたものをドライブに入れるだけにしたいとのことでした。
カンマは特定列だけで、桁数は変動ありなのでカンマの数はデータごとに異なります。
転記さえしてしまえばいいので、削除してもそのままでも問題ないです。
取り急ぎご回答いたしましたが、後ほど質問を編集しようかと思います!
> こちらのcsvですが、ウェブ上のサービスでダウンロードできる集計結果
そのようなCSVがカンマのために要素数が変わってしまうということもおかしいですね。
カンマ付きのデータが引用符で囲まれていたりはしないですか?
あるいは、元々10列のデータと7列のデータがあって、それらがあっていないだけだったというとはないのですね。
カンマの含まれる列があるというCSVは
1,2,3,"1,000,000",4,5,6
といった感じでダブルコーテーションで囲まれていたりするんですか?
それとも
1,2,3,1,000,000,4,5,6
というCSVだけど1,000,000は同じ列と言っているだけなのですか?
> そのようなCSVがカンマのために要素数が変わってしまうということもおかしいですね。
テスト用にcsvをいただくことができず、代わりに実施者がダウンロードしてきたcsvの内容をサンプルデータとしてスプレッドシートに貼られたものを私の方でcsvに貼り付けしてテストを実施していました。
ですので実施者が転記する際に、スプレッドシートのその列を数字表記にしてカンマがついてしまった可能性もありますので確認してみます。
> カンマ付きのデータが引用符で囲まれていたりはしないですか?
引用符などはないです。参考になるかわかりませんが、ファイルの種類はComma Separated SpreadSheet(.csv)です。
> 1,2,3,1,000,000,4,5,6
というCSVだけど1,000,000は同じ列と言っているだけなのですか?
ダブルクォーテーションはなく、後者になります。
Utilities.parseCSVは、引用符で囲まれると一つの要素にするので、多分バラバラになっているかと。
失礼しました!
他の形式で保存して確認してみたところ、該当列のデータは"10,000"のように囲まれておりました。
質問の意味を理解できておらずすみません。
> サンプルデータとしてスプレッドシートに貼られたものを私の方でcsvに貼り付けして
スプレッドシートからCSV形式でダウンロードするとカンマ付きの要素は引用符で囲まれるので、一つの列として扱われますよ。
今さらですが、2023/10/14 19:16の私のコメントは、YAmaGNZ様へのコメントでした。
> スプレッドシートからCSV形式でダウンロードするとカンマ付きの要素は引用符で囲まれるので、一つの列として扱われますよ。
こちら参考にさせていただきます。
カンマのエラーについては1つのファイルのみで起こる事象でしたので、実施者と相談してみます。
大変お手数ですが、もしわかる方いらっしゃいましたら、元質問のコードで思い通りの動作にするためのアドバイス等ありましたらご教示いただけますと幸いです。
参考までですが、
私のところでは動作しますが、
このスクリプトが希望どおりに動作しなくても
フォローアップは期待しないでください。
元の関数の
const valuea = Utilities.parseCsv(csv);
を
//次の行は、元の配列が7列で4列目がカンマ付きの列を1つにまとめるものです。
const values = repairParsedCsv(Utilities.parseCsv(csv), 7, 4 - 1);
としておき
次の新たな関数を保存します。
//カンマ付きデータで列数が増えている場合、その列の値をまとめる
//ただし、元のデータに空白を含む要素があると誤動作するので注意
//引数
// parsedCsv: 解析後の配列
// numColumns: 本来の列数
// index: カンマ付き列のインデックス(0始まり)
function repairParsedCsv(parsedCsv, numColumns, index) {
values = parsedCsv.map(v => {
let length = v.indexOf('');
length = length < 0 ? v.length : length;
const odd = length - numColumns;
if (odd) {
const result = [];
//カンマ付きの列の前まではそのまま格納
for (let i = 0; i < index; i++) {
result.push(v[i]);
}
//カンマ付きの列の最初の要素
let str = v[index];
//カンマ付きの列の残りの要素
for (let i = index + 1; i <= index + odd; i++) {
str += ',' + v[i];
}
//まとめた要素を格納
result.push(str);
if (index + odd < length) {
for (let i = index + odd + 1; i < length; i++) {
result.push(v[i]);
}
}
return result;
} else {
return v;
}
});
return values;
}
新たな関数は、
function EXCELtoSSAdd() {
...
...
}
の外側(最終行に追加するなど)にペーストしてください。
repairParsedCsv(Utilities.parseCsv(csv), 7, 4 - 1);
は、カンマがある列が2列目なら、
repairParsedCsv(Utilities.parseCsv(csv), 7, 2 - 1);
とします。

回答1件
あなたの回答
tips
プレビュー