カメリアの記事

意味があることやないことを綴ります

JavaScript で多次元配列の一次元化を reduce() を使わずにやってみた

嘘です。 reduce() を使わずにやったのではなく、知らないから使えなかったんです。この記事に存在意義はあるのか、そう自問します。いやいいんだよ、無駄なものなんてないさ。そう自答します。

コードは下にあります。その下には GitHub で公開しているところへのリンクがあります。気が向けばメンテナンスするでしょう。

説明

おおよそ二つの部分に分かれています。 breaker と sorter です。 breaker では多次元配列をどんどん崩していきます。崩れた順番が保証できない気がしたので sorter でソートします。

breaker の要点は、自分で自分を呼び出す再帰関数であるところでしょう。自分に食わすための引数を自分で吐き出す。頭がこんがらがってしまいます。

sorter の要点は、breaker に付けさせた「各階層ごとの順番を持った配列」の扱いです。 join() した値でソートするのですが、順番に二桁以上があったりすると単純に join() した値は使えません。桁数に合わせて 0 で埋めて処理しています。

コード

function dimensionBreaker(multidimArray) {
  let row = []
  return breaker(multidimArray, [])
  .then(rly => {
    return sorter(rly)
  })
  function breaker(target, id) {
    return new Promise(resolve => {
      if (!Array.isArray(target)) {
        resolve(row.push({"id": id, "not": target}))
      }
      else {
        let promiseArray = []
        for (let i in target) {
          promiseArray.push(breaker(target[i], id.concat(i.toString())))
        }
        Promise.all(promiseArray)
        .then(() => {
          resolve(row)
        })
      }
    })
  }
  function sorter(row) {
    let digits = []
    for (let i in row) {
      for (let j in row[i].id) {
        if (digits[j] === undefined) {
          digits[j] = 0
        }
        digits[j] = Math.max(digits[j], row[i].id[j].length)
      }
    }
    for (let i in row) {
      for (let j in row[i].id) {
        row[i].id[j] = `${"0".repeat(digits[j])}${row[i].id[j]}`.slice(1)
      }
   row[i].id.join("")
  }
  return row.sort((a, b) => a.id - b.id).map(rly => [rly.not])
 }
}

追伸

コードの終わりのほうでインデントに全角スペースを使っています。はてなブログのお馬鹿さんがどうしても改行を理解してくれなくて、仕方なくやったのです。僕とはてなを許してください。

github.com

JSZip でファイル名が文字化けするのを防ぐ

JSZip を使う際にファイル名を 2 バイト文字にしていると文字化するようです。 JavaScript のファイルのエンコードUTF-16 LE にしてやると防ぐことができます。気になるならスクリプトを呼ぶ際、下のようにしてやるといいかもしれません。

 <script src="..." charset="utf-16"></script>  

ラジオボタンの値を JavaScript でスマートに取得する

ラジオボタンでは複数のボタンから選択されたボタンの値を取得する必要があります。真正面からやると for 文を使ったりすることになりそうです。ですがちょっと工夫をしてスマートに取得してみました。

<label><input name="grp" value="1" checked>item 1</label>
<label><input name="grp" value="2">item 2</label>
<label><input name="grp" value="3">item 3</label>
let radio = document.quarySelectorAll(`[name="grp"]`)
let radioVal = Array.from(radio).filter(rly => rly.checked)[0].value

1行で書けましたね。ちょっとごちゃごちゃしていますがスマートにできているのではないでしょうか。

JavaScript でマッチするはずの 2 バイト文字にマッチしないとき

どう考えてもマッチするはずの正規表現がマッチしないとき、それはもしかしたら JavaScript を HTML の外部ファイルにしているからかもしれません。この場合はスクリプトを読み込むタグにキャラクターセットを記述すると正常に動作するようになります。下のように書きます。

<script src="..." charset="utf-8"></script>

CSS でサイズが決められない要素を JavaScript で中央に表示する

サイズの決まっている要素なら下のようにすると中央に表示することができます。

<div id="center">吾輩は猫である。</div>
#center {
    position: absolute;
    width: 600px;
    height: 400px;
    inset: 0;
    margin: auto;
  }

ですが、数文字の文を改行させないように表示するには、厳密には幅を事前に決めておくことができません。改行のある文章は、厳密には高さを事前に決めておくことができません。

JavaScript の出番です。下のようにすると中央に表示できます。

事前に CSS で設定しておくことができないので、JavaScript を使って、ブラウザが算出した要素のサイズを CSS として設定してやります。

inset について事前に CSS として設定しておくと、サイズの決まっていない要素は画面全体に広がってしまいます。 JavaScript でサイズを決定した後で設定してやる必要があります。

#center {
    position: absolute;
    margin: auto;
  }
window.addEventListener("DOMContentLoaded", () => {
    let center = document.getElementById("center")
    center.style.width = center.getBoundingClientRect().width
    center.style.height = center.getBoundingClientRect().height
    center.style.inset = 0
  })