表記ゆれに対応しました
s51517765.hatenadiary.jp
先週のこちらのこちらの記事の改良です。
Google spread sheetとGASを使って、オリジナルのレシピ検索システムを構築しました。
— とりてん (@s51517765) 2023年3月25日
「検索キー」にキーワードを入れると、タイトルか材料に一致するレシピを抽出します。
表記ゆれにも対応しました。
もちろんスマホ等からも動作します。 pic.twitter.com/y8sdkpYizn
どうしても検索キーの入力には「表記ゆれ」がつきものです。
動画に示している例のように、表記ゆれに対応しないと「鶏肉」とすべきところが「鳥」であったり「チキン」であったり微妙に違う単語を入力すると、思うような検索結果が得られません。
(動画では対応済みなので問題なく検索結果が出力されています。)
対応方法
以下のような形で対応しました。
①類義語のリストを準備する
②鳥に対して、[とり , トリ . チキン , 鶏 , 鳥]のようなリストを返す
③リストに対して検索をおこない、検索結果のすべてを返す
ただし、類義語は手動入力です。
また、漢字の読み仮名やカタカナ→ひらがなの変換は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(); }