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

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

ただいまの
回答率

88.80%

GASで重複していないデータを抽出したいです

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,624

ishikoro.1234

score 14

前提・実現したいこと

業務上、シート数の多いスプレッドシートにて翻訳する必要がございまして、仕事の効率化を捗るためにGASを勉強しています。
今回はシートごとに効率よく用語集の作成するために、GASを使ってスプレッドシート内の全てのシートを対象として、シートにあるデータの中に重複していないデータのみ抽出し、新しい行を追加して、重複していないデータを記入したいです。
プログラミングの初心者で、ネットで似たようなコードを検索してアレンジしていますが、どこから間違っているのかが分からなくて、ご教授頂ければ幸いです。

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

重複していないデータの抽出まではできましたが、なぜか配列をオブジェクトに変換できないとエラーがでました。
また、アクテイブシートを対象にし、重複したデータを取得できていますが、シートごとを対象にコードを書き換えてましたら、
「TypeError: (class)@2e5cac0fは関数ではなくオブジェクトです」と表示されました。

該当のソースコード

//var ss = SpreadsheetApp.getActiveSpreadsheet();
//var sheet = ss.getActiveSheet();

function readData() {
  var column = 1,  //対象列の指定
      lastRow = sheet.getLastRow(),  //最終列を取得
      columnRange = sheet.getRange(1,column,lastRow,2),  //範囲を取得
      rangeArray = columnRange.getValues();  //データを取得
  rangeArray = [].concat.apply([],rangeArray);  
 Logger.log(rangeArray);
 return rangeArray;
}

function findUnique(data) {
    var sortedData = data.slice().sort();  //データ配列をソート
    var unique = [];
    //var setCol = sheet.getLastColumn() + 1;  //データのある最終行を取得して、新しい行を追加する際に使う予定です

    for (var i = 0; i <= sortedData.length -1; i++) {

        if (sortedData[i + 1] != sortedData[i]) {  //重複していない文字を抽出
            unique.push(sortedData[i]);
}
    }
sheet.getRange(1,3).setValues(unique);
}

function main () {
  var SSLink = SpreadsheetApp.openById('対象スプレッドシートID');
  var sheet = SSLink.getSheets();
  var data = sheet.forEach(readData());
  var Unique = sheet.forEach(findUnique(data));
  //var data = readData();   アクテイブシートを対象にした場合に使っていました
  //var Unique = findUnique(data);

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+1

回答:

「TypeError: (class)@2e5cac0fは関数ではなくオブジェクトです」と表示されました。

呼出関数の引数に対して関数を渡さなければならない時にオブジェクトとなりえる要素を渡し実行しているためです。

そもそも、ご提示いただきましたソースコードをそのままGASで実行しましても関数「readData」内の[sheet.getLastRow()]の呼び出しでエラーとなり、ご質問頂いているエラーは発生しえません。

また、ご質問頂きましたのうち

シートにあるデータの中に重複していないデータのみ抽出し、

とありますが、この点「データ」とは何でしょうか?スプレッドシートの各シートごとの各セル内の入力された文字という意味合いでしょうか?

ご質問頂く場合、ご質問者様が認識なさっていることを具体的に細かくご提示いただきたく存じます。

ソースコードにつきましては間違っている部分が仰る通り初歩的な部分が多いですので、根本的にjavascriptの「文法」「戻り値」「関数」など基礎から学ぶべきかと存じます。

ただ、これでは不親切すぎますので、ユニークな文字列の抽出までのソース例(※)添付しておきます。
それ以降の挿入処理などはお調べつつ実装ください。
※あまりに大きいファイルは負荷がかかりますので、その辺りは自己責任でお願いいたします。

    /**
     * 引数で指定したSheetの、targetColumnで指定された列 および   全文字列要素を取得する
     * @param {Sheet}sheet データを取得するSheetオブジェクト
     * @param {number|undefined}targetColumn 指定する範囲の列番号
     * @param {number|undefined}targetLow 範囲の行数
     * @return {[].<String>} 当該のSheetで取得した文字列を格納したリスト
     */
    function getSheetValues(sheet , targetColumn , targetLow) {
        // targetColumn が無い場合 1 列をしてい
        if(!targetColumn)targetColumn = 1;
        // targetLow が無い場合 最終行を指定
        if(!targetLow)targetLow =sheet.getLastRow();
        // 今回指定した範囲のRangeデータを取得
        const columnRange = sheet.getRange(1,targetColumn,targetLow,2);  //範囲を取得
        // Rangeデータ内に含まれる全Valueを取得し、1つの配列要素に変換
        const rangeValueDataMultipleList =[].concat.apply([], columnRange.getValues());
        return rangeValueDataMultipleList;
    }

    /**
     * rangeValueDataMultipleList に含まれる文字列をユニークKeyとしたオブジェクトを取得する
     * @param {[].<String>} rangeValueDataMultipleList
     * @return {{}} ユニークな文字列をKey,Valueをtrueとしたオブジェクト
     */
    function findUniqueDataObject(rangeValueDataMultipleList) {
        const tempUniqueDataObject = {};
        const uniqueDataObject = {};
        // rangeValueDataMultipleListの各要素に含まれる文字列をKey、Valueをtrueとして uniqueDataObject オブジェクトに格納
        // オブジェクトに格納することで重複要素を
        const FIND =true;
        rangeValueDataMultipleList.forEach(function(str){
            // 既に重複している為,当該要素を削除
            if(tempUniqueDataObject[str] === FIND){
                delete uniqueDataObject[str];
            }else{
                tempUniqueDataObject [str] = FIND;
                uniqueDataObject[str] = FIND;
            }
        });
        return uniqueDataObject ;
    }


    /**
     * lastUniqueDataObjectに新しくユニークと判定した uniqueDataObjectの内容を追加する
     * @param {{}}lastUniqueDataObject
     * @param {{}}uniqueDataObject
     */
    function margeUniqueObjectData(lastUniqueDataObject , uniqueDataObject){
        const FIND =true;
        if(uniqueDataObject)
            for(var key in uniqueDataObject){
                // 既に要素があったら削除
                if(lastUniqueDataObject[key]){
                    delete lastUniqueDataObject[key]
                }else{
                    lastUniqueDataObject[key] = FIND;
                }
            }
    }


    /**
     * オブジェクトのKeyとなったデータを配列データとして変換する
     * @param convertObject
     * @return {Array}
     */
    function objectKeyToArray(convertObject){
        var convertedKeyArray = [];
        if(convertObject){
            convertedKeyArray =Object.keys(convertObject);
        }
            return convertedKeyArray;
    }

    function main () {
        const TARGET_SHEET_ID = "スプレッドシートID";
        const SSLink = SpreadsheetApp.openById(TARGET_SHEET_ID);
        const sheets = SSLink.getSheets();
        const uniqueDataObject = {};
        sheets.forEach(function(sheetObject){
            const rengeList = getSheetValues(sheetObject);
            margeUniqueObjectData(uniqueDataObject , findUniqueDataObject(rengeList));
        });
        Logger.log(uniqueDataObject);
        const convertedKeyArray = objectKeyToArray(uniqueDataObject);
        Logger.log(convertedKeyArray);
    }

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/05/29 13:20

    お忙しい中、ご教授及びアドバイスをくださり、誠にありがとうございます。
    「シートにあるデータ」の文章につきまして、説明不足で申し訳ございません、文字となります。
    また、ユニークな文字列の抽出までのソース例を添付してくださり、誠にありがとうございます。
    挿入処理につきまして、また可能であれば、重複していた場合も、一回のみその文字を記録するようにしたいので、自分なりに勉強して参ります。

    キャンセル

+1

仕様面での回答となります。

  • 単語帳となる(ブック→)シートを作る
  • 拾うのは単に文章ブックでユニークであることを越えて、単語帳と照らしあわせて考える
  • 単語帳はソートする(あー、でも漢語はうまくソートできないか)

動作検証するつもりなし

function q191750() {
    const dicSheetName = "glossary"
    const dicSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(dicSheetName)
    const dicEntries = dicSheet.getDataRange().map(pickFirstOfArray);
    const pickSpreadSheetId = "sheetid"
    SpreadsheetApp.openById(pickSpreadSheetId).getSheets()
      .map(function(e) { return e.getDataRange().map(pickFirstOfArray) })
      .reduce(function(a,c){ return a.concat(c)},[])
      .filter(function(e,i,a){ return a.indexOf(e) === i})
      .filter(function(e){ return dicEntries.indexOf(e) === -1 })
      .forEach(function(e) { dicSheet.appendRow([e]) })
    dicSheet.getDataRange().sort(1)
}
function pickFirstOfArray(e) {
    return e[0]
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/05/29 08:55

    変数名はdicなのにシート名はglossaryで少し笑いましたw

    キャンセル

  • 2019/05/29 11:54

    いつもお忙しい中、お時間を割ってご教授くださり、誠にありがとうございます。
    上記のコードを参考しながら、自分なりに研究して参ります。

    キャンセル

0

あんまりちゃんとコードを読んでないんですが
少なくとも

function readData(sheet) { // 引数にsheetが必要

/* 略 */

function main () {
  var SSLink = SpreadsheetApp.openById('対象スプレッドシートID');
  var sheets = SSLink.getSheets();  // 複数だから変数名を変更

  sheets.forEach(function(sheet) {
    findUnique(readData(sheet)); // findUniqueとreadDataは同じSheetに対する処理(?)だから同じループ内
  });
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 88.80%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る