概要
中身は標題のとおりですが、真新しいこと、技術的な知見はまったくありませんが、超簡単なわりには、事務的な作業には便利です。
Excelファイルを、簡単なPythonスクリプト(Pandas)でJsonに変換したものに、HTMLの簡単なUIをかぶせただけです。
Excelファイル
なんでもいいのですが、列名の一つは’Title’とすること、A列(0列目)は行番号にします。
ExcelからJsonへ
import pandas as pd
import os
df = pd.read_excel(r'./test.xlsx', index_col=0)
path = r"./test.json"
df.to_json(path, orient='records', force_ascii=False)
例えばこんなjsonファイルができます。
test.json
[
{"著者名":"杉崎 光一 and 阿部 雅人 and 全 邦釘","Title":"大規模言語モデルの専門領域への適用に関する検討","雑誌名":"AI・データサイエンス論文集","ISSN":"2435-9262","出版者名":"公益社団法人 土木学会","出版日付":2023,"巻":4,"号":3,"ページ":"474-481","URL":"https:\/\/cir.nii.ac.jp\/crid\/1390579599242174080","URL(DOI)":"https:\/\/doi.org\/10.11532\/jsceiii.4.3_474"},
{"著者名":"尾崎 大晟 and 中川 智皓 and 内藤 昭一 and 井之上 直也 and 山口 健史 and 新谷 篤彦","Title":"大規模言語モデルによる高品質反論文の自動生成","雑誌名":"人工知能学会全国大会論文集","ISSN":"2758-7347","出版者名":"一般社団法人 人工知能学会","出版日付":2023,"巻":"JSAI2023","号":0,"ページ":"4Xin111-4Xin111","URL":"https:\/\/cir.nii.ac.jp\/crid\/1390859758174987520","URL(DOI)":"https:\/\/doi.org\/10.11517\/pjsai.jsai2023.0_4xin111"},
{"著者名":"藤井 純一郎 and 大久保 順一 and 緒方 陸 and 天方 匡純","Title":"LLMを土木分野に適応するための基礎的研究","雑誌名":"AI・データサイエンス論文集","ISSN":"2435-9262","出版者名":"公益社団法人 土木学会","出版日付":-2034374400000,"巻":4,"号":3,"ページ":"779-785","URL":"https:\/\/cir.nii.ac.jp\/crid\/1390861074219063168","URL(DOI)":"https:\/\/doi.org\/10.11532\/jsceiii.4.3_779"},{"著者名":"宮下, 丈明 and 片山, 徹郎","Title":"テスト駆動開発における継続的な支援を目的としたフレームワークCATddの試作","雑誌名":"ソフトウェアエンジニアリングシンポジウム2023論文集","ISSN":null,"出版者名":"情報処理学会","出版日付":1692144000000,"巻":2023,"号":null,"ページ":"144-153","URL":"https:\/\/cir.nii.ac.jp\/crid\/1050015719934135168","URL(DOI)":null},
{"著者名":"玉置 亮太","Title":"特集 「日の丸LLM」に挑む : 生成AI安全保障は実現するか","雑誌名":"日経コンピュータ = Nikkei computer","ISSN":2854619,"出版者名":"東京 : 日経BP","出版日付":1692230400000,"巻":null,"号":1101,"ページ":"28-37","URL":"https:\/\/cir.nii.ac.jp\/crid\/1520015741622314112","URL(DOI)":null}
]
HTML
index.html
<!DOCTYPE html>
<html lang="ja">
<title>簡易検索</title>
<script type="module">
// urlから、jsonデータをfetchして取得する
async function getData(url) {
const response = await fetch(url);
const data = await response.json();
return data;
}
function searchObjectsByKeyword(data, keyword) {
const pattern = new RegExp(keyword, 'i'); // 大文字・小文字を無視
const result = [];
// オブジェクトや配列を再帰的に探索
function containsKeyword(obj) {
if (Array.isArray(obj)) {
return obj.some(item => containsKeyword(item));
}
return Object.values(obj).some(value => {
if (value === null || value === undefined) return false;
if (typeof value === 'string') {
return pattern.test(value);
} else if (typeof value === 'object') {
return containsKeyword(value);
}
return false;
});
}
// 検索本体 return data.filter(item => containsKeyword(item));
const filter_data = data.filter(item => containsKeyword(item));
return filter_data.map(item => {
// 不要なプロパティを削除
const newItem = {};
for (const key in item) {
if (item[key] !== null && item[key] !== undefined) {
newItem[key] = item[key];
}
}
return newItem;
});
}
// 結果表示を組み立てる
function createResult(keys2) {
let resultList2 = '';
resultList2 += '<ol class="resultHtml4">';
for (let i = 0; i < keys2.length; ++i) {
let resultHtml4 = craeteHtmlFromJson(keys2[i]);
resultList2 += resultHtml4 + '</li>'; // </ol>';
}
resultList2 += '</ol>';
return [resultList2, keys2.length];
}
function craeteHtmlFromJson(keys2i) {
let resultHtml3 = [];
const title = keys2i['Title'] || 'No Title';
resultHtml3.push(`<li class="title">${title}</li><ul class="hidden hidden2">`);
for (const [key, value] of Object.entries(keys2i)) {
resultHtml3.push(`<li class="${key}">${key} ${value}</li>`);
}
resultHtml3.push('</ul>');
return resultHtml3.join('');
}
// console.log('要素を消す');
const deleteElem = (elem) => {
let items = document.querySelectorAll(elem); // '.resultHtml4');
for (const item of items) {
item.remove();
}
}
const url = './test.json'; // ここにjsonファイルのURLを指定
const data = await getData(url);
// 入力フォーム
const input = document.getElementById("sampleForm");
const span = document.getElementById("inputCounter");
input.addEventListener("keyup", function () {
span.textContent = input.value.length;
if (span.textContent > 2) {
let search_word = input.value;
const result = searchObjectsByKeyword(data, search_word); // ←検索したい単語を指定
console.log('result:', result);
const resultList = createResult(result);
document.getElementById('resultHtml').innerHTML = resultList[0];
document.getElementById('resultTotal').innerHTML = '<div class="total">Total: ' + resultList[1] + '件です。</div>';
document.getElementById('clickBtn1').innerHTML = '<input type="button" value="詳細表示" id="clickBtn1" />';
} else {
console.log('ここで要素を消す');
deleteElem('.resultHtml4');
deleteElem('.total');
};
});
</script>
<style>
.hidden {
display: none;
}
</style>
</head>
<body>
<label for="sampleForm">検索
<ul>
<li>3文字以上入力すると検索を開始します。</li>
<li>正規表現が使えます(2文字で検索したい場合、ドットを加えることで可能)。</li>
</ul>
</label>
<br />
<input type="text" id="sampleForm" placeholder=".*大規模.*"/>
<p><span id="inputCounter">0</span>文字</p>
<div id="clickBtn1"></div>
<div id="resultTotal"></div>
<div id="resultHtml"></div>
<script>// ...existing code...
// 詳細表示をトグルする
const btn = document.getElementById('clickBtn1');
btn.addEventListener('click', () => {
const categories = document.getElementsByClassName('hidden2');
Array.from(categories).forEach(e => {
if (e.classList.contains('hidden')) {
e.classList.remove('hidden');
} else {
e.classList.add('hidden');
}
});
});
</script>
</body>
</html>
一応解説
- jsonファイrをfetchしてきて、オブジェクトをfilter()しているだけです。
- 完全にローカルだけで動きますが、適切なところにおけばjsonファイルはインターネットのどこかでも大丈夫です。
- 3文字入力すると、インクリメンタルに検索します。
- 正規表現が使えるので、2文字でも dot をつければ検索できます。
- だだし、元のエクセルで列が違えば、掛け合わせはできません。