お知らせ

現在サイトのリニューアル作業中のため、全体的にページの表示が乱れています。
投稿日:
開発::設計言語::JavaScript

自動生成されたHTMLから社員情報を引っこ抜くスクリプトを作った時に考えたことを書き出してみる。ブラウザのDevToolsを使った簡易ツールを書いていて、頭に浮かんだことだ。データやコードは架空の内容なので実在しない。

ベースのHTML

大まかに最初に目に入ってきたのはこんな内容だった。機械生成されているせいかフォーマットがガタガタ気味だ。

<span class="Content">
    <span id="undefined">SHAIN.XLS page1</span>
</span>
<span class="Content">
    <br>
    <span id="undefined">社員リスト</span>
    <span id="undefined"></span>
</span>
<span class="Content">
    <br>
    <span id="hoge1">00001</span>
    <span id="fuga1">:山菱喜一</span>
</span>
<span class="Content">
    <br>
    <span id="hoge2">00002</span>
    <span id="fuga2">:鈴木与一</span>
</span>
<span class="Content">
    <br>
    <span id="hoge3">00011</span>
    <span id="fuga3">:茨城妙子</span>
</span>
<span class="Content">
    <span id="undefined">SHAIN.XLS</span>
    <span id="undefined"> page2</span>
</span>
<span class="Content">
    <br>
    <span id="hoge4">00012</span>
    <span id="fuga4">:奈良楓</span>
</span>
...

まずは関数ベースで考える

ひとまず普段は関数ベースで実装しているので関数ベースで実装する。書いていてgetEmployee(name, list)の仮引数にあるlistは冗長ではないかということに気づく。

const emp_list = document.getElementsByClassName('Content');
[...emp_list].reduce((acc, cur) => {
  if (cur.id === 'undefined' || cur.children.length !== 3) {
    return acc;
  } else {
    const id = cur.children[1].innerText;
    const name = cur.children[2].innerText.replace(/^:/, '');
    if (/^\d+$/.test(id)) {
      acc.push({ id, name });
    }

    return acc;
  }
}, []);

const getEmployee = (name, list) => {
  const re = new RegExp(`.*${name}.*`);
  const result = list.filter((emp) => re.test(emp));
  console.log(result);
};

クラスにしてみる

そしてクラスにしたほうが素直にならないかと考え、クラスにしてみた。getEmployee(name, list)search(name)となり、冗長性が減ったほか、抽象的な表現で分かりやすくなった気がする。

class EmployeeSearch {
  constructor() {
    const emp_list = document.getElementsByClassName('Content');
    this.list = [...emp_list].reduce((acc, cur) => {
      if (cur.id === 'undefined' || cur.children.length !== 3) {
        return acc;
      } else {
        const id = cur.children[1].innerText;
        const name = cur.children[2].innerText.replace(/^:/, '');
        if (/^\d+$/.test(id)) {
          acc.push({ id, name });
        }

        return acc;
      }
    }, []);
  }

  search(name) {
    const re = new RegExp(`.*${name}.*`);
    const result = this.list.filter((emp) => re.test(emp.name));
    console.log(result);
  }
}

追加仕様に合わせ関数を新設し、クラスを改良する

動作確認をしていく中で表示と名前が分かれたパターンを発見したので、これに合わせて直してゆく。

<span class="Content">
    <br>
    <span id="hoge2">00002</span>
    <span id="fuga2">:飯田</span>
    <span id="piyo2">太郎</span>
</span>

この対応をしようとすると分岐が必要だと考えたが、愚直にやってしまうとコンストラクタの中がごちゃつく上、テストもしづらいと考え、DOMをパースする処理を外部関数に切り出し、それを呼ぶことにした。

const getEmployeeFromList = (domList) => {
  const id = domList[1].innerText;
  const name1 = domList[2].innerText.replace(/^:/, '');
  if (domList.length === 3) {
    return {
      id,
      name: name1
    }
  } else {
    const name2 = domList[3].innerText;
    return {
      id,
      name: `${name1}${name2}`
    }
  }
};

class EmployeeSearch {
  constructor() {
    const emp_list = document.getElementsByClassName('Content');
    this.list = [...emp_list].reduce((acc, cur) => {
      if (cur.id === 'undefined' || cur.children.length < 3) {
        return acc;
      } else {
        const { id, name } = getEmployeeFromList(cur.children);
        if (/^\d+$/.test(id)) {
          acc.push({ id, name });
        }

        return acc;
      }
    }, []);
  }


  search(name) {
    const re = new RegExp(`.*${name}.*`);
    const result = this.list.filter((emp) => re.test(emp.name));
    console.log(result);
  }
}

更により良くするには?

ブラウザのDevToolsにあるConsoleで書いたコードなので、現実問題テストコードも書かないし、そこまで凝る必要もないのが、よりよい設計にするにはコンストラクタでパースせず、事前にパース処理した結果をコンストラクタを通じて注入して処理するようにするとよいだろう。そうすると責務が分離され、テストがしやすく、コードもきれいになると考える。

クラスにするか、関数にするか

これはまだ正直答えが見えていないが、インスタンスを引き回す場合はクラスのほうが都合がいい可能性がある気がしてきている。

関数でもできるにはできるが、毎回引数にインスタンス相当のものを渡すのは冗長である可能性がある。疎結合という文脈では関数のほうがより良い可能性もあるが、人間の認知負荷や、保守性との兼ね合いだと思う。

明確な損益分岐点はまだ見えていないし、今回のケースだと割とどっちでもいい部分もあるとは思う。

投稿日:
ジャンル::生活ジャンル::買い物

キッチンペーパーホルダーと皿立てを買った。

まずここ数年、箱や袋型のキッチンペーパーを使っていたのだが、中身が減ってくると取り出しづらかったし、場所をとるので割と邪魔だった。そこでロール式に買い替えたのだが、そのままでは取り出しづらい問題があった。横置きにしていれば転がるし、縦置きにしていれば自転しないし、場所をとる問題も大きく改善しない。

20250727_185212557.JPG

というわけでダイソーでキッチンペーパーホルダーを買ってきた。これは戸棚や鴨居に引っ掛けられるフックを上下左右90度方向にはめ込むことが出来、いろんな方向に設置できる優れものだ。

分解しないと交換できない製品もある中、抜き差しだけで交換できる部分もよい。

20250727_185345120.JPG

配置の微妙さは否めないが、しばらく使ってみたい。

20250727_184751332.JPG

他には皿立ても買った。これもダイソー。

20250728_095453295.JPG

元々は重ね置きをしていたのだが、これだと取り出しづらく埃が溜まりやすい問題があったので、その辺りを改善できないかと買ってみた。皿を引っ張り出すときに皿置きがずれ落ちて皿が全滅する可能性を若干心配するが、奥に置いているのと、自重による摩擦抵抗がそこそこあるので大丈夫だと信じたい。

20250727_224641415.JPG

ただ深皿の配置が微妙なのと、汁椀を置いたところさらに微妙になったので、これは微妙かもしれない…。

投稿日:
ジャンル::買い物

クローゼットの棚をPCバッグや登山リュックが占拠していて、何とかならないかと思っていたが、それを解決するグッズを買った。

20250609_122448231.JPG

買ったのは清水産業のハンガーパイプから落ちない幅広フックという製品だ。

20250609_122621332.JPG

こんな感じで空いている空中スペースを有効活用しカバン類を収納できるので重宝している。

投稿日:
ジャンル::買い物地域::香川県

香川漆器を買ったでも書いたが、スプーンの損耗が激しいので買い直すことにした。ついでに菓子切も買った。

20250726_211930512.JPG

今回購入したのはこちら。菓子切は高松の漆器工、中田漆器のもので竹の拭き漆。スプーンは三重県桑名市の籐芸という木のスプーンを専門に作っている会社の品で、材質はインドネシア産のサオの木とのこと。サオの木とはサワノキのことで、インドネシアの木だそうだ。

20250726_212505219.JPG

判り辛いがスプーンは曲面部分が完全な曲面でなく、多角形のようになっているようで、角の筋目が見えるのが面白い。形的にも以前より自然なので使いやすそうだ。

20250726_212432497.JPG

菓子切は五本もあるが、ひとまずはこの一本を使うことにした。五本あって選べるのは贅沢だ。

20250726_213205041.JPG

これまでに買ったものと合わせると漆器もだいぶ増えてきた。

ただどうも漆器のスプーンは長持ちしないという話をちょいちょい見聞きするので、これもどれほど持つのかは謎である。前のよりは確実に持つと思うが…。

ひとまず漆器シリーズはいい感じに揃ってきたので、あとは育てていきたい感じだ。

投稿日:
ジャンル::買い物ジャンル::生活

洗濯機で洗えないものや、それを使うまでもない汚れを落とすのに便利そうだよなというので買ってみた。

20250726_121204408.JPG

というわけで百均のmeetsで買ってきた洗濯板がこちら。

使用感としては想定していた通りで、トートバックの取っ手や裾についてた軽い汚れを落とすのには便利だった。手揉み洗いと比べると遥かに効率的に洗える。

服に何か小さい汚れが付いたときに素早く対応するのにも使えそうだ。