Menu
CoddyTech

検索

CoddyのNode.jsジャーニー「Node.jsの基礎」セクションの一部。レッスン 32/33。

数十件のメモがあると、listをスクロールして探すのは難しくなります。検索が必要です。

比較する前に両方を小文字にすれば、Milk、milk、MILKはすべて一致します。その後、includesでテストします。リスト全体をループして各インデックスを保持すれば、一致した項目にはlistと同じ番号が表示されます。これはdoneとremindが期待する番号です。

const songs = ['Blue Moon', 'Moonlight', 'Sunny'];
const query = 'MOON'.toLowerCase();
songs.forEach((title, i) => {
    if (title.toLowerCase().includes(query)) {
        console.log(`${i + 1}. ${title}`);
    }
});
challenge icon

チャレンジ

簡単

メモがたくさんあると、listをスクロールして探すのは難しくなります。単語でメモを検索するsearchコマンドを追加しましょう。

  1. notes.jsにsearchコマンドを追加します。searchの後に続く単語が検索対象です。
  2. 大文字・小文字を問わず、検索文字列を含むすべてのメモを見つけます。milk、Milk、MILKのいずれでもBuy milkが見つかります。
  3. formatNoteを使って、一致したメモをそれぞれ表示します。メモの番号にはlistでの番号を使うので、その番号をdoneやremindで使えます。一致したメモは、保存されている順に古いものから表示します。settings.jsonの設定でlistが新しい順に並ぶ場合も同様です。見出しは表示しません。

メモがBuy milk、Call Ada、buy breadの場合、node notes.js search buyを実行すると次のように表示されます。

1. Buy milk
3. buy bread

node notes.js search ADAを実行すると、2. Call Adaと表示されます。

  1. 一致するメモがない場合は、No notes match の後に検索文字列を表示します。node notes.js search teaを実行すると、次のように表示されます。
    No notes match tea

試してみましょう: $ node notes.jsの後に表示されるボックスにsearch milkと入力してEnterキーを押し、次にsearch MILKと入力します。どちらも同じメモが見つかります。次に、どのメモにも含まれていない単語を検索してみましょう。No notes matchとその単語が表示されます。

自分で試してみよう

// メモツールはここから始まります。
import { rmSync } from 'node:fs';
import path from 'node:path';
import { formatNote } from './format.js';
import stats from './legacy/stats.cjs';
import pkg from './package.json' with { type: 'json' };
import store from './store.js';

const [command, ...words] = process.argv.slice(2);
const text = words.join(' ');

store.on('added', (note, n) => {
    console.log(`Saved note ${n}.`);
});

const noteNumber = (word) => {
    const n = Number(word);
    if (!Number.isInteger(n) || n < 1 || n > store.notes.length) {
        console.error(`No note ${word}`);
        process.exit(1);
    }
    return n;
};

if (command === 'add') {
    await store.add(text);
} else if (command === 'list') {
    const owner = process.env.NOTES_OWNER ?? 'you';
    console.log(`Notes for ${owner}`);
    if (store.notes.length === 0) {
        console.log('No notes yet');
    }
    const lines = store.notes.map((note, i) => formatNote(i + 1, note));
    if (store.settings.sort === 'newest') {
        lines.reverse();
    }
    lines.forEach((line) => console.log(line));
} else if (command === 'where') {
    const file = path.join(process.cwd(), 'notes.json');
    console.log(file);
    console.log(path.basename(file));
} else if (command === 'stats') {
    console.log(`${store.notes.length} notes, ${stats.countWords(store.notes)} words`);
} else if (command === 'done') {
    const removed = await store.remove(noteNumber(words[0]));
    console.log(`Done: ${removed}`);
} else if (command === 'remind') {
    const note = store.notes[noteNumber(words[0]) - 1];
    let count = 3;
    const timer = setInterval(() => {
        if (count > 0) {
            console.log(count);
            count -= 1;
        } else {
            console.log(note);
            clearInterval(timer);
        }
    }, 200);
} else if (command === 'import') {
    let input = '';
    for await (const chunk of process.stdin) {
        input += chunk;
    }
    const lines = input
        .split('\n')
        .map((line) => line.trim())
        .filter((line) => line !== '');
    for (const line of lines) {
        await store.add(line);
    }
    console.log(`Imported ${lines.length} notes.`);
} else if (command === 'version') {
    console.log(`${pkg.name} ${pkg.version}`);
} else if (command === 'clear') {
    rmSync('notes.json', { force: true });
    console.log('Deleted notes.json');
} else {
    if (command) {
        console.error(`Unknown command: ${command}`);
    }
    console.error('Usage: node notes.js <command> [words]');
    process.exit(1);
}

Node.jsの基礎のすべてのレッスン

自分で練習してみよう: Expressプレイグラウンド