プログラミング素人のはてなブログ

プログラミングも電気回路も専門外の技術屋の末端が勉強したことや作品をアウトプットするブログ。コードに間違いなど見つけられたら、気軽にコメントください。 C#、Python3、ラズパイなど。

Google spread sheetでオリジナルのレシピ検索システムを作る(2)

表記ゆれに対応しました

s51517765.hatenadiary.jp
先週のこちらのこちらの記事の改良です。

どうしても検索キーの入力には「表記ゆれ」がつきものです。
動画に示している例のように、表記ゆれに対応しないと「鶏肉」とすべきところが「鳥」であったり「チキン」であったり微妙に違う単語を入力すると、思うような検索結果が得られません。
(動画では対応済みなので問題なく検索結果が出力されています。)

対応方法

以下のような形で対応しました。
①類義語のリストを準備する
②鳥に対して、[とり , トリ . チキン , 鶏 , 鳥]のようなリストを返す
③リストに対して検索をおこない、検索結果のすべてを返す
ただし、類義語は手動入力です。

また、漢字の読み仮名やカタカナ→ひらがなの変換はAPIがあったので、これも使いました。
しかしながら、debugでは上手く動くのですが、onEdit(e)でシート上から呼び出すと上手く動かない場合があるようです。
上手く応答が返ってこないときは、入力をそのまま返します。

function GetPhonetic(word) {
  //https://technical.verybestcbp.com/getphonetic/
  //word = "ハルサメ"
  //よみたんAPIに「かな」の読みがなを要求
  try {
    let url = "https://yomi-tan.jp/api/yomi.php?ic=UTF-8&oc=UTF-8&k=" + "h" + "&n=3&t=" + word

    //よみたんAPIの応答から1個目を取得
    let phonetic = UrlFetchApp.fetch(url).getContentText().split(",")[0]
    //console.log("よみ : " + phonetic);
    return phonetic;
  }
  catch {
    return word;
  }
}

表記ゆれパターンはVariationシートに横方向へあらかじめ準備しておき、入力されたキーワードをそのリストの中から探索し、見つかったらその行をリストとして返します。

function getPattern(keyword) {
  var varSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Variation");
  let range = varSheet.getDataRange().getValues();
  //console.log(range);
  for (let row of range) {
    for (var c = 0; c < row.length; c++) {
      if (row[c] == keyword) {
        let row2 = row.filter(function (element) {
          return element !== "";
        });
        //Keywordがみつかれば、表記ゆれリストを返す
        //console.log("row : ", row);
        return row2;
      }
    }
  }
  //Keywordが見つからなければKeywordを返す
  return [keyword];
}

ここで、範囲取得が長方形で取得されるらしく、空白セルもリストの要素として取得される場合があります。
それについては、以下のように除外します。

let row2 = row.filter(function (element) {
  return element !== "";
});

あとは

for (let searchString of pattern) {}

のように検索をします。

まとめ

自作のレシピ検索システムを表記ゆれに対応しました。
コード全体は以下のようになります。

//Shift+Alt+F 整形

function main() {
  var listSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("List");
  var filterSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Filter");
  let range = listSheet.getRange("A:A");
  var values = range.getValues();
  var TargetRow = 3;
  var upperCount;
  var lowerCount;
  var array = [];
  var keyword = filterSheet.getRange(1, 3).getDisplayValue();
  var range2 = filterSheet.getRange("A:A");

  // 列をクリア
  range2.clearContent();

  if (keyword=="")return;

  //よみがな
  var kana = GetPhonetic(keyword);

  filterSheet.getRange(1, 4).setValue("検索中");

  //表記ゆれパターンを取得
  var pattern = getPattern(kana);
  console.log("pattern : ", pattern);

  if (pattern.length != 0) {
    values.unshift("");//配列のインデックスと行を一致
    for (var i = 1; i < values.length; i++) {
      for (let searchString of pattern) {
        //console.log("searchString : ", searchString)
        if (values[i].toString().indexOf(searchString) !== -1) {
          upperCount = 1;
          lowerCount = 1;
          array.push(values[i]);
          while (values[i - upperCount] != "") {
            array.unshift(values[i - upperCount]);
            upperCount += 1;
          }
          while (values[i + lowerCount] != "") {
            array.push(values[i + lowerCount]);
            lowerCount += 1;
          }
          array.push("")//区切り

          //console.log(array);
          for (var j = 0; j < array.length; j++) {
            filterSheet.getRange(TargetRow, 1).setValue(array[j]);
            TargetRow += 1;
          }
          array = [];
          i += lowerCount;
        }
        else if (values[i].toString().indexOf("EOF") !== -1) {
          filterSheet.getRange(1, 4).setValue("完了");
          return;
        }
      }
    }
  }
  filterSheet.getRange(1, 4).setValue("完了");
}


function GetPhonetic(word) {
  //https://technical.verybestcbp.com/getphonetic/
  //word = "ハルサメ"
  //よみたんAPIに「かな」の読みがなを要求
  try {
    let url = "https://yomi-tan.jp/api/yomi.php?ic=UTF-8&oc=UTF-8&k=" + "h" + "&n=3&t=" + word

    //よみたんAPIの応答から1個目を取得
    let phonetic = UrlFetchApp.fetch(url).getContentText().split(",")[0]
    //console.log("よみ : " + phonetic);
    return phonetic;
  }
  catch {
    return word;
  }
}

function getPattern(keyword) {
  var varSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Variation");
  let range = varSheet.getDataRange().getValues();
  //console.log(range);
  for (let row of range) {
    for (var c = 0; c < row.length; c++) {
      if (row[c] == keyword) {
        let row2 = row.filter(function (element) {
          return element !== "";
        });
        //Keywordがみつかれば、表記ゆれリストを返す
        //console.log("row : ", row);
        return row2;
      }
    }
  }
  //Keywordが見つからなければKeywordを返す
  return [keyword];
}

function onEdit(e) {
  try {
    let range = e.range;
    if (range.getRow() != 1 || range.getColumn() != 3) {
      return;
    }
  }
  catch {
    //pass
  }
  main();
}