Panda Noir

JavaScript の限界を究めるブログでした。最近はいろんな分野を幅広めに書いてます。

rowspanを考慮してtable要素を2次元配列に変換する(第一正規化する)

rowspan、colspanがあるとtd:nth-of-type などがズレてしまう。

td:nth-of-type(2) に背景色をつけた図

html

<table>
  <tr>
    <td>col1,1</td>
    <td rowspan="3">col1,2</td>
    <td>col1,3</td>
  </tr>
  <tr>
    <td>col2,1</td>
    <td>col2,2</td>
  </tr>
  <tr>
    <td>col3,1</td>
    <td>col3,2</td>
  </tr>
  <tr>
    <td>col4,1</td>
    <td>col4,2</td>
    <td>col4,3</td>
  </tr>
</table>

2列目に色をつけたいのに、col2,2col3,2にも色がついてしまっている。本来はこうなってほしい↓

2列目だけに色がついている図

これをするには第一正規化してやればよい。正規化する関数 normalizeTable の実装はこちら。

const normalizeTable = (table: HTMLTableElement) => {
  const rows = table.querySelectorAll<HTMLTableRowElement>('tr');
  const tableCells = Array.from(rows).map((row) =>
    Array.from(
      row.querySelectorAll<
        HTMLTableDataCellElement | HTMLTableHeaderCellElement
      >('td, th'),
    ),
  );

  for (let r = tableCells.length - 1; r >= 0; r--) {
    const row = tableCells[r];
    if (!row) continue;
    for (let c = row.length - 1; c >= 0; c--) {
      const cell = row[c];
      if (!cell) continue;
      if (cell.rowSpan === 1 && cell.colSpan === 1) continue;
      for (let i = 0; i < cell.rowSpan; i++) {
        for (let j = 0; j < cell.colSpan; j++) {
          if (i === 0 && j === 0) continue;
          tableCells[r + i]?.splice(c + j, 0, cell);
        }
      }
    }
  }
  return tableCells;
};

tableCells[row][col] には見た目の位置どおりのtd要素が入っている(rowspanが指定されたtdは複数箇所に入ってる)。あとは、normalizeTable を使って適当にクラスを振ってやればよい。

const cells = normalizeTable(document.querySelector('table'))
for (const row of cells) {
  for (let i = 0; i < row.length; i++) {
    row[i].classList.add(`col${i}`);
  }
}

これで td.col1 に背景色をつけてやれば意図した通りにうごく

転職活動の結果報告 + 転職の動機とか

ひとまず結果が出揃ったので報告です(※まだ転職先は決定してないです)。

合計12社受けました。 内訳がこちら↓

  • 1次落ち: 4社 (内訳: エージェント2社、スカウト1社、自己応募1社)
  • 2次落ち: 1社 (スカウト1社)
  • 最終落ち: 2社 (スカウト2社)
  • 内定: 5社 (スカウト4社、エージェント1社)

カジュアル面談を合わせると 20は超えてるはず。めちゃくちゃ受けたな…

時系列

  • 去年11月: カジュアル面談を受け始める
  • 今年1月: 選考を受け始める
  • 2月中旬: 退職時期を決める
  • 2月末: 受けていた5社すべてに落ちる (最終2社、1次3社)
  • 3月: 7社受け始める
  • 4月: 初内定
  • 4月18日: 最後の選考結果が出る (5社内定)

2月までに受けていた5社が全滅して、結構驚きました。もっとすんなり決まるだろうと油断してた ので(スカウトも結構もらってたし)。退職時期も先に決めていたため、このときはかなり焦りました。

全落ちを受けて反省した(別記事)ので、後半7社では 「1、2社から内定きたら御の字だな」 と思いながら受け始めました。でも今度は7社中5社から内定をいただけました。 じゃあ前半の惨敗はなんだったんだよ。

所感: スカウトだと通りやすい

やっぱり スカウトされた選考が通りやすかった ですね。おそらく「入ってもらうならココ!」と最初からポジションがイメージされていた、というのも大きいです。

スカウトされた中では1社だけ1次で落ちましたが、そこはポジションが全然噛み合ってなかったです(インフラまで範囲だった)。噛み合えばそこもいけてたかも。

エージェント経由で受けたところは本当につんつるてん でした。1社しか通らなかったです。なんでなのかは結局はっきりとは分かりませんでした。純粋に僕が面接下手くそでスカウトという下駄履かせてもらわないとダメ説はあります。

今回の転職の動機

今回の転職の動機についても書いておきます。でもこれが とてもややこしいんですよね… 選考でも聞かれたし、周りからもめちゃくちゃ聞かれたんですが、毎回答えるのに困りました。

端的にまとめると、

  • 最終的なモチベーションは 「新潟にUターンしたいから」
  • 転職を考えたきっかけは 「 LY社(現職)の週1出社」 (に関連する意思決定フローの不透明さ、モチベーションに関する説明不足、施策の妥当性への納得感の欠如など)

転職のきっかけ: 週1出社

去年12月にLY社(現職)が週1出社を決定したのを受け、カジュアル面談を受け始めました。 正直そのときはメチャクチャ会社にブチギレてました。 もう1秒たりとも居てたまるかってくらい。出社させるモチベもわからないしやり方も納得できなかったです。出社したくないというより、社員のことを考えてなさそうに見えたのが大きかったです。

ただ、最終選考で某社の社長とお会いして話したときに、LY社の週一出社のモチベーションについてウワサを聞いて、モチベについては一応納得しました (たぶん社外秘にあたるので詳細は伏せます)。聞いた内容的には社員を考えてない訳では無さそうでした。まあ方法論に関してはまだ納得してないですが…

というわけなので、いまは LY社には全然キレてません。 なんなら良い会社ではあったなと思います(出社の件はもっとやりようがあったと思いますが)。僕の政治的思想(アンチ東京一極集中)と合わなくなったのもあって離れる決意をしただけで、馴染むのであれば 全然オススメできる会社です。 けっこう伸び伸びと働けましたし、ここまで大きく成長できたのは絶対にLINE社に入れたおかげです。

最終的なモチベーション: 新潟にUターンしたいから

というわけで転職のモチベとしては「地元に戻る」のが一番大きいんですが、実は 当初は全然Uターンを考えてませんでした。 転職するにしても都内のままと思ってました。でも、転職という選択肢が頭にある状態で正月に帰省したとき、 「新潟めっちゃいいな…」 と思いました。これが大きかったです。正月の帰省がなかったら、週一出社のモチベに納得した時点で転職をやめていたかもしれません。

東京という街が嫌い(独身の街だから)という思想もUターンと噛み合いました。もう十分東京で遊んだなと思うので、これ以上はいても仕方ないです。たぶん向こう5年は新潟います。新潟いいですよ。めっちゃ住みやすい。

出社動機まとめ

以上をまとめると、

  1. 当初は週一出社にブチギレていたから転職しようと思っていたが、
  2. 選考の途中で週一出社のモチベーションについて知って納得して、
  3. 最後は「新潟へUターンしたい」のがモチベーションになった

これが今回の転職の動機の全容です。

給与上げたいとか環境変えたいみたいな理由は殆どないです。現職がちょっと上振れているので、どこを選んでも待遇面は多少下がると思います。

まとめ: 今後について

前半はメンタルがボロボロになってきつかったですし、後半も週に5回選考があったりかなり忙しくて大変でした。もうしばらくは転職活動なんてしたくない……

今後はひとまずGW前後を目安に会社を決めて、新潟での物件を選んで、7月あたりに引っ越しって感じで予定してます。5月から1ヶ月半は有給消化期間ですが、とはいえ意外と余裕がないです。ワタワタしそう。

あと、おそらく転職関連の記事はこれが最後です。入社エントリも退職エントリも今のところ書くつもりないので。

機能別のECMAバージョン対応表

ある機能がどのECMAバージョンで入ったのかの一覧表。ある程度グルーピングをしてある。

2025年現在のproposalsのfinished-proposals.mdを参照している。

(なお、全部確認はしたはずだが、間違ってる可能性もある。抜け等あったらコメントお願いします🙏)

Array

Atomic

Class

Function

Module

Object

Operator

Promise

RegExp

String

TypedArray

Other

propsが変更されたときにすべてのstateをリセットするHOCを作る

react.devの「そのエフェクトは不要かも」のprops が変更されたときにすべての state をリセットするに書かれている解決策が微妙に感じたので、改善案を提案します。

そもそも: 公式の解決策がなぜ微妙に感じるのか?

props が変更されたときにすべての state をリセットするでは、propが変更されたときに必ずstateをリセットする制約を作るために、子コンポーネントへkeyを設定するだけのラッパーコンポーネントを作ろう と提案してます。

↓(修正前) userId propが更新されたとき、useEffectを使って comment stateをリセットしているコンポーネント

export default function ProfilePage({ userId }) {
  const [comment, setComment] = useState('');

  // 🔴 Avoid: Resetting state on prop change in an Effect
  useEffect(() => {
    setComment('');
  }, [userId]);
  // ...
}

↓(修正後) userId propが更新されたとき、keyを使って comment stateをリセットしているコンポーネント

export default function ProfilePage({ userId }) {
  return (
    <Profile
      userId={userId}
      key={userId}
    />
  );
}

function Profile({ userId }) {
  // ✅ This and any other state below will reset on key change automatically
  const [comment, setComment] = useState('');
  // ...
}

…まあ言いたいことは理解できるんですが、 コンポーネントを分けなきゃいけないところが微妙に感じます。

  • Profileを単体でみたとき「userIdが変わったらcommentがリセットされる」という事情が読み取れない
  • 分割後の子コンポーネントの名前の付け方が難しい

このように 公式の解決策には問題があります。 なので、代わりにHOCを作ればいいんじゃないか?というのが本記事の提言です。

別の解決策: withResetOnPropChangeというHOCを作る

要はkeyを指定するだけのラッパーを作れたらよいのですから、それをする高階コンポーネント(Higher-Order Component)を作ればよさそうです。こんな感じです↓

const withResetOnPropChange =
  <Props extends Record<string, unknown>>(Component: FC<Props>, key: string) =>
  (props: Props) => <Component key={props[key] as string} {...props} />;

これを使うと上のProfilePageはこのように書けます。

export default withResetOnPropChange(function ProfilePage({ userId }) {
  // ✅ This and any other state below will reset on key change automatically
  const [comment, setComment] = useState('');
  // ...
}, 'userId');

コンポーネントの分割もなくなり、コードの意図もシンプルに読み解けるようになりました。個人的にはかなり良いんじゃないかなと思ってます。

転職選考の前半でやらかした失敗7選

1月から転職のために選考を受け始めました。しかし、まさかの 4社連続1次落ち を経験しました。そのタイミングで猛省をした結果、最終的に 4社から内定をいただきました。 (まだ1社選考残ってますが)

今回は特に転職活動の前半で「やらかしたな〜」と思った点を7つほど紹介します(多すぎる)。

(追記: 本記事は「自分のココができてなかったな〜」というのが趣旨です、特定の企業に対して落ち度があった等は思っていません。僕が選考のいろはが分かってなかったのが落ちた理由の8割だったと思ってます)

失敗7つ

  • 応募先ポジションとのミスマッチ
  • 自己アピール不足
  • 職務経歴書を隅々まで読まれてる前提で受けていた
  • フロント専門が思ってた以上に少ない
  • スカウトされたという話が通ってる前提で話していた
  • 退職日を先に決めた
  • 手当たり次第に全部受けた

それぞれ以下に書いていきます

失敗1: 応募先ポジションとのミスマッチ

当然ですが、募集してるポジションとこっちの想定が違うと落ちます。 特に注意すべきなのは「フルスタックエンジニア」という募集です。自分はインフラやDBはほぼ触らないつもりで応募したのに、向こうは最初からインフラまでガッツリできると想定していた、みたいなミスマッチがよく起きました。

対策: カジュアル面談でポジションのすり合わせをする。

「選考に進むとしたらどういうポジションになりますか」とカジュアル面談で聞きましょう。僕の場合は「実務経験はほぼウェブフロントとサーバーのアプリコードのみです」と伝え、それでも応募可能かを聞いてました。

失敗2: 自己アピール不足

選考のどのタイミングで自己アピールするのか分かってませんでした。 まず最初に自己紹介があり、そこでアピールもすべきらしいです。これも気づくのが遅れました。愚直に職歴しか話さないのは勿体ないです。

また、次の「職務経歴書を隅々まで読まれてる前提で受けていた」とも繋がりますが、基本的な技術力があると事前に知られてると思い込んでいたのも失敗でした。職務経歴書とは被っててもいいから自己アピールをすべきです。

対策: 自己紹介ターンでしっかりアピールする。相手はなにも知らない前提で自己アピールする。

失敗3: 職務経歴書を読まれてる前提で基本的な技術力をアピールしなかった

職務経歴書、zenn、githubがすべてザックリとは見られてる前提で受けていました。 そのため、「基本的な技術力は伝わっているだろう」と過信してました。バカでした。

技術力は必ずアピールする必要があります。 最初から伝わってるなんて過信は絶対ダメです。仮に向こうが事前に読んでいて技術力を把握していても、繰り返しアピールして良いはずです。

1次面接(技術面接)では基礎的な技術力が重点的に見られる傾向があります。なのに技術力をアピールしなかったらそりゃ落ちます。提出したzennやgithubの内容と重複していても良いので、ちゃんとアピールしましょう。

チーム開発で意識してることや自分なりのバリューを出せる部分など、応用的な内容については二次以降で話せば十分です。

対策: 一次面接では基本的な技術力をアピールする。

失敗4: フロント専門が思ってた以上に少ない

ウェブフロントが一番長いので、基本的にウェブフロントで探してましたが、思ってた以上に募集が少ないです。基本的にフルスタックでの募集でした。大手にはフロント専門がありましたが、フルリモートでないことが多かったです。

対策: ウェブフロント以外の領域もしっかり伸ばしておく。 今回はなんとかなりましたが、今後を考えると、潰しが効くように他の領域もやったほうが良さそうでした。

失敗5: スカウトされたという話が通ってる前提で話していた

スカウトされて選考に進んでるという情報は、まず伝わってない前提で動いたほうが良いです(伝わってることもありますが)。なので、一次面接の時点で「誰々さんから誘われて選考を受けました」と話すと良いです。

まあ、コレは選考結果に直結するほどではなさそうなので、「話が伝わってる」という思い込みだけしてなければ問題ないと思います。

失敗6: 退職日を先に決めた

チームの事情とか組織再編の影響で、退職日を先に決めてから転職活動をしました。が、あとで調べたら転職先が決まる前にやめるのは結構リスクがありました。たとえば無職期間がマイナスに査定されたり、社会保険料を追加で払わないといけなかったり、保険の手続きを自分でしなきゃいけなかったり……

結果的に退職前に内定が出たのでよかったですが、 不必要なリスクを背負わないほうがよいです。

対策: 在職中に内定を得て、そのあと退職日を決める。

失敗7: 手当たり次第に全部受けた

5社連続で落ちて「もうだめだ…」となってしまい、次に6社並行で受け始めたら、今度は6社ほぼ全部通ってしまいました。1、2社から内定来たら御の字と思ってたため「どこにしよう…」とメチャクチャ嬉しい悩みを抱えることになりました。内定4つも出たけどほんとどうしよう…

退職日を先に決めていて焦っていたのもありますが、内定が多すぎるとそれはそれでメチャクチャ困ります。 気をつけたほうが良いです。内定は意外と出ます。6社受けて0だったとしても、そこから4社連続内定とか来ます。

「選考受けるぶんには無料」と調子に乗ってはいけません。たくさん受けると時間コストが馬鹿にならないので。

対策: 選考はちゃんとスケジュールを立てて受ける。

番外編: 意外と問題なかったこと

逆に、意外と問題なかったな〜ということもいくつかあります。

  • 「社会課題の解決には興味ありません」と答えた
  • 服装

問題なかったこと: 「社会課題の解決には興味ありません」と答えた

「御社の医療現場の課題を解決するというところに惹かれました」みたいなのはよくありますが、僕は一貫して「課題さえ解決できればよくて、課題自体はなんでもよい」と言ってました。これでも通りました。

問題なかったこと: 服装

基本的に全部オンラインで選考を受けましたが、服装はそこまで気にされてなかったと思います。スーツを着ないで無地のTシャツなどで受けてましたが問題なく選考を通りました。最終選考までそれでいきました。そもそも面接官も同じような服装でした。

というか、仮に自分が技術面接の担当だったら服装は評価基準にしないと思います。よほどじゃなければ。

まとめ: 選考は油断してはならない

新卒選考で撃沈した苦い過去があったので働きながらどうしたら良かったかは結構考えていました。それでも前半はツンツルテンでした。難しいですね。

失敗を反省したあとは内定がドサドサと出たのでよかったですが、もう二度と転職なんてしたくないかも……