Programming Self-Study Notebook

勉強したことを忘れないように! 思い出せるように!!

Base32ってのもあるらしい

f:id:overworker:20200406080809j:plain:h250


Base32とは

任意のデータを32種類の英数字に変換するエンコード方式。

  • 大文字、小文字の区別がない環境でも利用可能
  • 40bit(5文字)を8文字に変換していく
  • 40bitに満たない場合は=でパディングする
種類
文字 A~Z 26種
数字 2~7 6種
合計 合計 32種
その他 = パディング文字

base32のビット列と英数字の変換テーブル

10進 2進 文字 10進 2進 文字
0 00000 A 26 11010 2
1 00001 B 27 11011 3
2 00010 C 28 11100 4
3 00011 D 29 11101 5
4 00100 E 30 11110 6
5 00101 F 31 11111 7
6 00110 G
7 00111 H
8 01000 I
9 01001 J
10 01010 K
11 01011 L
12 01100 M
13 01101 N
14 01110 O
15 01111 P
16 10000 Q
17 10001 R
18 10010 S
19 10011 T
20 10100 U
21 10101 V
22 10110 W
23 10111 X
24 11000 Y
25 11001 Z

その他の記事について

overworker.hatenablog.jp

Base64についての事前学習

f:id:overworker:20200406080809j:plain:h250


JavaScriptはたまに使う』程度の筆者による、メモ書き程度の内容です。

Base64とは

任意のデータを64種類の英数字に変換するエンコード方式。

  • 取扱いが可能なことが多い半角英数字(64種)に変換したうえでデータを扱う
種類
小文字 a~z 26種
大文字 A~Z 26種
数字 0~9 10種
記号 +,/ 2種
合計 合計 64種
その他 = パディング文字

特徴

  • 文字列型の変数に確認可能
  • データサイズは大きくなる
    • データ量は4/3(約133%)になる
    • MIMEの基準では76文字ごとに改行コードが入るため、この分の2バイトを計算に入れるとデータ量は約137%となる

具体例

目的 電子メールを使って画像データや音声データをやり取りする
制約 電子メールのSMTPプロトコルはASCII((7bit)しか送ることができない(MIME
手順 ①送信側があらかじめBase64フォーマット(半角英数字)に変換する(エンコード
②送信側がSMTPプロトコル上にBase64フォーマットのデータ(半角英数字)を格納し転送する
③受信側がBase64データをデコードする。
目的 JSONなどで画像データを保存する
制約 利用できるデータ形式がString形、倍精度浮動小数点型しかない
手順 ①あらかじめBase64フォーマット(半角英数字)に変換する(エンコード
JSONのプロパティにBase64フォーマットのデータ(半角英数字)を格納する
③データを利用する者はプロパティに格納されたBase64データをデコードしたうえで利用する。

JavaScriptBase64を使うとは

  • JavaScriptで取り扱いずらい画像データや音声データのようなバイナリデータをJavaScriptで取り扱いやすい「文字列(Base64)」に変換して処理(移動、保存、複製等)をすること。

変換アルゴリズム(→Base64

// ---------------
// 元の文字列
// ---------------
"abcdefg"

 ↓
// ---------------
// 手順1.
//    エンコードしたいバイナリデータを6bitずつ取り出す(足りない分は0を追加する)。
// ---------------
0x61 0x62 0x63 0x64 0x65 0x66 0x67  // (16進数表記)
 ↓
01100001 01100010 01100011 01100100 01100101 01100110 01100111  // (2進数表記)
 ↓
// 6bitずつに分解する(2進数表記)
011000, 010110, 001001,100011, 011001, 000110, 010101, 100110, 011001, 11 
 ↓
// 6bitに満たない場合は0で埋める
011000, 010110, 001001,100011, 011001, 000110, 010101, 100110, 011001, 110000

 ↓
// ---------------
// 手順2.
//    Base64の変換テーブルを用いて`英数字`に変換する
// ---------------
Y W J j Z G V m Z w

 ↓
// ---------------
// 手順3.
//    文字数が4の倍数になるように不足部分に`=`を追加する
// ---------------
YWJj ZGVm Zw==

 ↓
// ---------------
// 完成:Base64データ(文字列)
// ---------------
"YWJjZGVmZw=="

base64のビット列と英数字の変換テーブル

10進 2進 文字 10進 2進 文字 10進 2進 文字 10進 2進 文字
0 000000 A 26 011010 a 52 110100 0 62 111110 +
1 000001 B 27 011011 b 53 110101 1 63 111111 /
2 000010 C 28 011100 c 54 110110 2
3 000011 D 29 011101 d 55 110111 3
4 000100 E 30 011110 e 56 111000 4
5 000101 F 31 011111 f 57 111001 5
6 000110 G 32 100000 g 58 111010 6
7 000111 H 33 100001 h 59 111011 7
8 001000 I 34 100010 i 60 111100 8
9 001001 J 35 100011 j 61 111101 9
10 001010 K 36 100100 k
11 001011 L 37 100101 l
12 001100 M 38 100110 m
13 001101 N 39 100111 n
14 001110 O 40 101000 o
15 001111 P 41 101001 p
16 010000 Q 42 101010 q
17 010001 R 43 101011 r
18 010010 S 44 101100 s
19 010011 T 45 101101 t
20 010100 U 46 101110 u
21 010101 V 47 101111 v
22 010110 W 48 110000 w
23 010111 X 49 110001 x
24 011000 Y 50 110010 y
25 011001 Z 51 110011 z

参考文献

JavaScriptの文法(例外とエラー処理)

f:id:overworker:20200406080809j:plain:h250

Errorオブジェクト

// インスタンスの生成
const err = new Error('エラーを検出しました。');

以下はサブルーチンで実際にErrorオブジェクトを返却する例です。

// Email Addressに対するバリデーションを実施する
function validateEmail(email){
  if (email.match(/@/)){
    return email;
  } else {
    if (email === 'no_message'){
      return new Error();
    } else if(email === 'empty_message'){
      return new Error('');
    } else {
      return new Error(`Email Address Invalid:${email}`);
    }
  }
}

function checkResult(result){
  if(result instanceof Error){
    if(result.message){
      return result.message;
    } else {
      if (result.message === null){
        return 'null';
      } else if (result.message === ''){
        return 'empty';
      } else if (result.message === undefined){
        return 'undefined!';
      } else if (result.message === 0){
        return 0;
      }
    }
  } else {
    return `Email Address OK!:${result}`;
  }
}

let email = "";
let validatedEmail = {};
email = "no_message";
validatedEmail = validateEmail(email);
console.log(checkResult(validatedEmail)); // empty
email = "empty_message";
validatedEmail = validateEmail(email);
console.log(checkResult(validatedEmail)); // empty
email = "test_gmail.com";
validatedEmail = validateEmail(email);
console.log(checkResult(validatedEmail)); // Email Address Invalid:test_gmail.com
email = "test@gmail.com";
validatedEmail = validateEmail(email);
console.log(checkResult(validatedEmail)); // Email Address OK!:test@gmail.com

上記例からわかったこと - サブルーチン等からエラーが返ってきていないことを確認するにはinstanceofを使用する - エラーオブジェクトのインスタンスに何もセットしなかった場合、.message''(空文字列)相当

例外処理(try...catch)

let email = "";
let validatedEmail = {};
try {
  email = null;
  validatedEmail = validateEmail(email);
  console.log(checkResult(validatedEmail)); // 実行されない
} catch {
  console.log(`catch Exception :${email}`); // catch Exception :null
}
  • エラーをキャッチしているので、プログラムは停止しない。
    • エラーを記録してプログラムを継続する。

例外のスロー

  • JavaScriptでは任意の値(数値、文字列、オブジェクト等)をthrowすることが可能
    • 一般的(慣例的)にはErrorオブジェクトのインスタンスthrowする

例外処理とコールスタック

function a (){
  console.log('a:開始');    // 出力あり
  b();
  console.log('a:終了');    // 出力無し
}
function b (){
  console.log('b:開始');    // 出力あり
  c();
  console.log('b:終了');    // 出力無し
}
function c (){
  console.log('c:開始');    // 出力あり
  throw new Error('c error');
  console.log('c:終了');    // 出力無し
}

console.log('処理を開始します');
try{
  a();
  console.log('処理を終了します');
} catch (err){
  console.log('----------------Catch Exception');
  console.log(`err.message:${err.message}`);  // err.message:c error
  console.log('--err.stack:');
  console.log(`${err.stack}`);
  /*
  Error: c error
    at c (main.js:22)
    at b (main.js:17)
    at a (main.js:12)
    at HTMLButtonElement.<anonymous> (main.js:28)
  */
}
  • .stackthrowの実行場所、及びthrowまでの経緯を確認することができる。

try...catch...finally

Errorの発生の有無にかかわらず必ず実施しなけらばならない処理(必ず実施したい処理)はfinallyに記述する

console.log('処理を開始します');
try{
  a();
  console.log('この行は例外発生時は処理されません');
} catch (err){
  console.log('Catch Exception');
} finally {
  console.log('処理を終了します');
}

例外処理は例外に限る

  • 例外は正しく処理されないとプログラムがクラッシュすることもある。
  • 例外処理のコストは馬鹿にならない。

参考文献

JavaScriptの文法(マップとセット)

f:id:overworker:20200406080809j:plain:h250

せっかくMapSetが使えるのだからObjectを使うと決定する前に、MapSetが使えないか?を考えるべき。

マップ(Map)

  • オブジェクトの特徴(マップの違い)
    • キーとして使用できるのは、文字列シンボルのみ。(オブジェクトをキーとすることができない)
    • キーと値の組がいくつあるか、簡単に分からない。
    • プロトタイプがあるため、意図しないマッピングが生じる危険性がある。
// 前処理
const u1 = {name:'Takeshi'},
      u2 = {name:'Hanako'},
      u3 = {name:'Daichi'},
      u4 = {name:'Keiko'};

// マップ(Mapオブジェクト)の生成
const userRoles = new Map();

// メソッドsetを用いてユーザーと役割の対応付け
userRoles.set(u1,'teacher')
          .set(u2,'student')
          .set(u3,'student');

// --------------------- 
//  Mapの中身を確認する
// --------------------- 

// get
console.log(userRoles);         // Map(3) {{…} => "teacher", {…} => "student", {…} => "student"}
console.log(userRoles.get(u1)); // teacher
console.log(userRoles.get(u2)); // student
console.log(userRoles.get(u3)); // student
console.log(userRoles.get(u4)); // undefined

// has
console.log(userRoles.has(u1)); // true
console.log(userRoles.has(u2)); // true
console.log(userRoles.has(u3)); // true
console.log(userRoles.has(u4)); // false

// set(上書き)
console.log(userRoles.set(u3,'ob')); // Map(3) {{…} => "teacher", {…} => "student", {…} => "ob"}

// sizeを取得する
console.log(userRoles.size); // 3

// 要素に対しアクセスする
for(let u of userRoles.keys())
  console.log(u.name);
  // Takeshi
  // Hanako
  // Daichi
for(let r of userRoles.values())
  console.log(r);
  // teacher
  // student
  // ob
for(let ur of userRoles.entries())
  console.log(`${ur[0].name}: ${ur[1]}`);
  // Takeshi: teacher
  // Hanako: student
  // Daichi: ob
for(let [u,r] of userRoles.entries())
  console.log(`${u.name}: ${r}`);
  // Takeshi: teacher
  // Hanako: student
  // Daichi: ob
for(let [u,r] of userRoles)// 「entries」はデフォルトイテレータ
  console.log(`${u.name}: ${r}`);
  // Takeshi: teacher
  // Hanako: student
  // Daichi: ob

// Valueを抽出する
console.log(userRoles.values());      // MapIterator {"teacher", "student", "ob"}
console.log([...userRoles.values()]); // ["teacher", "student", "ob"]

// マップから要素を削除する
userRoles.delete(u2);
console.log(userRoles.size);      // 2
console.log(userRoles.values());  // MapIterator {"teacher",  "ob"}
console.log(userRoles);           // Map(2) {{…} => "teacher", {…} => "ob"}

// マップから全ての要素を削除する
userRoles.clear();
console.log(userRoles.size);      // 0
console.log(userRoles.values());  // MapIterator {}
console.log(userRoles);           // Map(0) {}

ウィークマップ(WeakMap)

セット(Set)

  • 重複しない
// セット(Setオブジェクト)の生成
const roles = new Set();

// メソッドaddを用いてユーザーと役割の対応付け
roles.add('teacher');
console.log(roles);       // Set(1) {"teacher"}
roles.add('student');
console.log(roles);       // Set(2) {"teacher", "student"}

// プロパティsizeを用いてサイズを確認する
console.log(roles.size);  // 2

// 既存の要素に重複する要素を追加する
console.log(roles.add('student'));
console.log(roles);       // {"teacher", "student"}

// 要素を削除する
console.log(roles.delete('student'));   // true
console.log(roles.size);                // 1
console.log(roles);                     // Set(1) {"teacher"}

console.log(roles.delete('ob'));        // false
console.log(roles.size);                // 1
console.log(roles);                     // Set(1) {"teacher"}

console.log(roles.delete('teacher'));   // true
console.log(roles.size);                // 0
console.log(roles);                     // Set(0) {}

ウィークセット(WeakSet)

参考文献

JavaScriptの文法(配列)

f:id:overworker:20200406080809j:plain:h250

配列の基本

  • 0から始まる数字(添え字、インデックス)によってアクセスする。
    • 順序を持っている(オブジェクトのプロパティとは違う)
  • JavaScriptの配列には異なる型の要素を入れることができる
    • オブジェクトや他の配列を格納することが可能
  • 配列リテラル[]を使って表現される。
    • 配列要素にも[]を使ってアクセスする。
  • lengthというプロパティーで配列の要素を知ることができる
  • 配列の最後の要素の添え字よりも大きな数字を使って代入を行うと、配列が自動的に大きくなり、値が指定されていない要素にはundefinedが暗黙のうちに代入される。
  • Arrayコンストラクタを使うこともできる(しかしあまり使われない。)

配列要素の操作

  • 配列の要素を変更してしまうメソッド(破壊的なメソッド)と新しい配列を返すメソッドがある。

先頭・最後の要素に対する操作

配列arrの先頭の要素 配列arrの最後の要素
arr[0] arr[arr.length-1]
メソッド 機能 返却値 備考
push 配列の最後に要素を追加する 変更後の配列の長さ 配列そのものを変更する
pop 配列の最後の要素を削除する 削除された要素 配列そのものを変更する
unshift 配列の先頭に要素を追加する 変更後の配列の長さ 配列そのものを変更する
shift 配列の先頭の要素を削除する 削除された要素 配列そのものを変更する

複数要素の追加

メソッド 機能 引数 返却値 備考
concat 複数の要素を配列に追加する 追加する要素 配列のコピー 破壊なし
  • 引数の個数は任意
  • 引数に配列をセットすると、配列を分解して元の配列の最後に追加する
    • ただし、引数の配列の要素が配列の場合、内側の配列は分解しない
let before = [1,2,3];
console.log(before);              // [1,2,3]
let after = before.concat(4,5,6);
console.log(after);               // [1,2,3,4,5,6]
after = before.concat([4,5,6]);
console.log(after);               // [1,2,3,4,5,6]
after = before.concat([4,5],6);
console.log(after);               // [1,2,3,4,5,6]
after = before.concat([4,5],[6,7]);
console.log(after);               // [1,2,3,4,5,6,7]
after = before.concat([4,5,[6,7]]);
console.log(after);               // [1,2,3,4,5,Array(2)] ->[1,2,3,4,5,[6,7]]

部分配列

メソッド 機能 引数 返却値 備考
slice 既存の配列の
一部を切りだす
①:切り出す開始位置
②:終了位置
配列のコピー 破壊なし
  • 第二引数で指定された要素の直前までを切り出す
    • 第二引数を指定しない場合は、最後の文字まで切り出す
  • 引数に負の値をセットすると、最後の要素から数える
let before = [1,2,3,4,5];
console.log(before);              // [1,2,3,4,5]
let after = before.slice(3);
console.log(after);               // [4,5]
after = before.slice(2,4);
console.log(after);               // [3,4]
after = before.slice(-2);
console.log(after);               // [4,5]
after = before.slice(1,-2);
console.log(after);               // [2,3]
after = before.slice(-2,-1);
console.log(after);               // [4]

途中の要素の削除や途中の要素への追加

メソッド 機能 引数 返却値 備考
splice 指定した位置の
要素(複数可)を削除し、
新たな要素を追加する
①:変更開始位置
②:削除する要素の数(0指定可)
③~:追加する要素
取り除かれた要素の配列 配列そのものを変更する
// 配列(splice)
let arr = [1,5,7];
console.log(arr);               // [1,5,7]
let res = arr.splice(1,0,2,3,4);
console.log(arr);               // [1,2,3,4,5,7]
console.log(res);               // []
res = arr.splice(5,0,6);
console.log(arr);               // [1,2,3,4,5,6,7]
console.log(res);               // []
res = arr.splice(1,2);
console.log(arr);               // [1,4,5,6,7]
console.log(res);               // [2,3]
res = arr.splice(2,1,"a","b",);
console.log(arr);               // [1,4,"a","b",6,7]
console.log(res);               // [5]

配列内の要素の削除や置換

メソッド 機能 引数 返却値 備考
copyWithin 指定した位置に
指定した位置の要素をコピーする
(上書きする)
①:コピー先開始位置
②:コピー元
(③:コピーを終了する場所)
新たに生成された配列 配列そのものを変更する
  • 第二引数、第三引数には負の値を指定することがあできる。
  • 配列の要素数は変化しない
    • コピー元データのサイズが大きく、上書き対象が存在しない場合は、はみ出た部分のみが無視される。
let origin = [1,2,3,4];
let after = origin.copyWithin(1,2);
console.log(origin);               // [1,3,4,4]
console.log(after);                // [1,3,4,4]
after = origin.copyWithin(2,0,2);
console.log(origin);               // [1,3,1,3]
console.log(after);                // [1,3,1,3]
after = origin.copyWithin(0,-3,-1);
console.log(origin);               // [3,1,1,3]
console.log(after);                // [3,1,1,3]

let origin2 = [1,2,3,4,5,6,7,8,9];
let after2 = origin2.copyWithin(6,0,6);
console.log(origin2);               // [1,2,3,4,5,6,1,2,3]
console.log(after2);                // [1,2,3,4,5,6,1,2,3]

let origin3 = [1,2,3,4,5,6,7,8,9];
let after3 = origin3.copyWithin(4,5,8);
console.log(origin3);               // [1,2,3,4,6,7,8,8,9]
console.log(after3);                // [1,2,3,4,6,7,8,8,9]

配列を特定の値で埋める

メソッド 機能 引数 返却値 備考
fill 複数の要素の値を一度に指定する ①:要素にセットしたい値
(②:開始位置)
(③:終了する場所)
新たに生成された配列 配列そのものを変更する
  • 第二引数、第三引数には負の値を指定することがあできる。
  • 配列の要素数は変化しない
    • コピー元データのサイズが大きく、上書き対象が存在しない場合は、はみ出た部分のみが無視される。
let origin = new Array(5).fill(1);
console.log(origin);               // [1,1,1,1,1]
let after = origin.fill("a");
console.log(origin);               // ["a","a","a","a","a"]
console.log(after);                // ["a","a","a","a","a"]
after = origin.fill("b",1);
console.log(origin);               // ["a","b","b","b","b"]
console.log(after);                // ["a","b","b","b","b"]
after = origin.fill("c",2,4);
console.log(origin);               // ["a","b","c","c","b"]
console.log(after);                // ["a","b","c","c","b"]
after = origin.fill(5.5,-4);
console.log(origin);               // ["a",5.5,5.5,5.5,5.5]
console.log(after);                // ["a",5.5,5.5,5.5,5.5]
after = origin.fill("e",-3,-1);
console.log(origin);               // ["a",5.5,"e","e",5.5]
console.log(after);                // ["a",5.5,"e","e",5.5]

逆転とソート

メソッド 機能 引数 返却値 備考
reverse 配列の要素を逆転させる 新たに生成された配列 配列そのものを変更する
let origin = [1,2,3,4,5];
console.log(origin);               // [1,2,3,4,5]
let after = origin.reverse();
console.log(origin);               // [5,4,3,2,1]
console.log(after);                // [5,4,3,2,1]
after = origin.reverse();
console.log(origin);               // [1,2,3,4,5]
console.log(after);                // [1,2,3,4,5]
メソッド 機能 引数 返却値 備考
sort 配列の要素を並べ替える 新たに生成された配列 配列そのものを変更する
  • sortの結果は昇順となる?
    • 降順が取得したい場合はsortの結果に対してreverseを実施する
let origin = [5,3,2,4,1];
console.log(origin);               // [5,3,2,4,1]
let after = origin.sort();
console.log(origin);               // [1,2,3,4,5]
console.log(after);                // [1,2,3,4,5]
origin.reverse();
console.log(origin);               // [5,4,3,2,1]
console.log(after);                // [5,4,3,2,1]
  • 一般的にオブジェクトもプロパティを指定して並び替えることが可能らしいが、Chromeで以下のコードを実施してみたところ変化がなかった
let origin = [{name:"Satoshi"},{name:"Takeshi"},{name:"Akira"},{name:"Koji"}];
console.log(origin); // [{name:"Satoshi"},{name:"Takeshi"},{name:"Akira"},{name:"Koji"}]
origin.sort((a,b) => a.name > b.name);
console.log(origin); // [{name:"Satoshi"},{name:"Takeshi"},{name:"Akira"},{name:"Koji"}]
origin.sort((a,b) => a.name[1] > b.name[1]);
console.log(origin); // [{name:"Satoshi"},{name:"Takeshi"},{name:"Akira"},{name:"Koji"}]

検索

indexOf(指定した値と最初に一致する要素の添え字番号を返す)、
lastIndexOf(指定した値と最後に一致する要素の添え字番号を返す)

メソッド indexOf、(lastIndexOf)
内容 指定した値と最初(最後)に一致した要素の添え字の番号を返却
比較方法 厳密比較
該当あり時の動作 添え字の番号を返却
該当なし時の動作 -1を返却
オプション 検索開始位置を指定することができる
const o = {name:"Takeshi"};
const arr = [true,5,8,"2","4","f","G",[1,2],o,null,,true,false,5,8,"2"];

console.log(arr.indexOf(5))                 // 1
console.log(arr.indexOf("f"))               // 5
console.log(arr.indexOf(4))                 // -1
console.log(arr.indexOf("4"))               // 4
console.log(arr.indexOf({name:"Takeshi"}))  // -1
console.log(arr.indexOf(o))                 // 8
console.log(arr.indexOf([1,2]))             // -1
console.log(arr.indexOf(null))              // 9
console.log(arr.indexOf(false))             // 12
console.log(arr.indexOf(undefined))         // -1
console.log(arr.indexOf(true))              // 0

console.log(arr.lastIndexOf(5))             // 13
console.log(arr.lastIndexOf("f"))           // 5

findIndex(指定した条件と最初に一致した要素の添え字の番号を返却)

メソッド findIndex
内容 指定した条件と最初に一致した要素の添え字の番号を返却
比較方法 関数内で指定
該当あり時の動作 添え字の番号を返却
該当なし時の動作 -1を返却
const arr = [{id:5,name:"Takeshi"},{id:7,name:"Hanako"}];

console.log(arr.findIndex(o => o.id === 7));                  // 1
console.log(arr.findIndex(o => o.name === "Hanako"));         // 1
console.log(arr.findIndex(o => o === 7));                     // -1
console.log(arr.findIndex(o => o === "Hanako"));              // -1
console.log(arr.findIndex(o => o === {id:7,name:"Hanako"}));  // -1
console.log(arr.findIndex(o => o == {id:7,name:"Hanako"}));   // -1
console.log(arr.findIndex(o => o === arr[1]));                // -1
console.log(arr.findIndex(o => o.id === 7));                  // 1
console.log(arr.findIndex(o => o.id === "7"));                // -1
console.log(arr.findIndex(o => o.id == "7"));                 // 1

find(指定した条件と最初に一致した要素を返却)

メソッド find
内容 指定した条件と最初に一致した要素を返却
比較方法 関数内で指定
該当あり時の動作 要素自体が返却される
該当なし時の動作 undefinedを返却
オプション 第二引数:添え字
第三引数:配列全体
const arr = [{id:5,name:"Takeshi"},{id:7,name:"Hanako"}];

console.log(arr.find(o => o.id === 7)); // {id:7,name:"Hanako"}
console.log(arr.find(o => o.id === 6)); // undefined

添え字を条件(処理の実態)で使用する例

const arr = [1,18,16,5,25,16,10,3,49,9];

console.log(arr.find((x,i) => i>2 && Number.isInteger(Math.sqrt(x)))); // 25

関数呼び出し時にthisの内容を指定する例

class Person{
  constructor(name){
    this.name = name;
    this.id =Person.nextId++;
  }
}
Person.nextId = 0;
const a = new Person("一郎"),
b = new Person("次郎"),
c = new Person("三郎"),
d = new Person("四郎");
const arr = [a,b,c,d];

// IDを使って直接比較
console.log(arr.find((p) => p.id === c.id));                       // Person {name: "三郎", id: 2}

// thisを定数"c"に指定
console.log(arr.find(function (p) { return p.id === this.id}, c)); // Person {name: "三郎", id: 2}

some(指定した条件と一致する要素の有無の判定を実施する)

メソッド some
内容 指定した条件と一致する要素が一つ以上存在することの判定を実施する
比較方法 関数内で指定
該当あり時の動作 trueが返却される
該当なし時の動作 falseを返却
const arr = [5,7,10,11,19];
console.log(arr.some(x => x%2 === 0));                      // true
console.log(arr.some(x => Number.isInteger(Math.sqrt(x)))); // false

every(すべての要素が指定した条件を満たすかの判定を実施する)

メソッド every
内容 すべての要素が指定した条件を満たすかの判定を実施する
比較方法 関数内で指定
該当あり時の動作 trueが返却される
該当なし時の動作 falseを返却
const arr = [4,6,16,36];
console.log(arr.every(x => x%2 === 0));                      // true
console.log(arr.every(x => Number.isInteger(Math.sqrt(x)))); // false

mapとfilter

map(配列内の要素を変換する)

メソッド map
内容 配列内の要素を変換する
変換方法 関数内で指定
レスポンス 新しい配列(コピー)が返却される
const lineup = [{type:"iPhone",price:54800},{type:"Android",price:49800}];
const types = lineup.map(x => x.type);
console.log(types);  // ["iPhone","Android"];
const prices = lineup.map(x => x.price);
console.log(prices);  // [54800,49800];

// priceを2割引きした、新しい配列を作成する
const lineup2 = lineup.map(x => ({
  type:x.type,
  price:x.price*0.8,
}));
console.log(lineup2); // [{type:"iPhone",price:43840},{type:"Android",price:39840}]

添え字を処理の実態で使用する例

const types = ["iPhone","Android"];
const prices = [54800,49800];
const lineup =types.map((x,i) => ({
  type: x,
  price: prices[i],
}));
console.log(lineup);

filter(配列から要素を取り去る)

メソッド filter
内容 配列内の要素を削除する
変換方法 関数内で指定
レスポンス 新しい配列(コピー)が返却される
const cards = [];
const suits = ["ハート","ダイヤ","クラブ","スペード"];
for(let suit of suits){
  for(let i= 1;i<=13;i++){
    cards.push({
      suit:suit,
      number:i,
    })
  }
}
console.log(cards);
const selected = cards.filter(card => card.number === 2);
console.log(selected);

/* selected
0: {suit: "ハート", number: 2}
1: {suit: "ダイヤ", number: 2}
2: {suit: "クラブ", number: 2}
3: {suit: "スペード", number: 2}
*/

// ここから組み合わせ
const selected2 = cards.filter(card => card.number === 1).map(x => ({suit:x.suit,number:"A"}));
console.log(selected2);

/* selected
0: {suit: "ハート", number: "A"}
1: {suit: "ダイヤ", number: "A"}
2: {suit: "クラブ", number: "A"}
3: {suit: "スペード", number: "A"}
*/

reduce(全要素に対する処理を実施した最終結果を返す)

メソッド reduce
内容 全要素に対する処理を実施した最終結果(任意の値)を返す
変換方法 関数内で指定
レスポンス 任意の値が返却される
補足 返却値の型は、関数内で何をセットするかで決まる
補足 アキュムレータの初期値は第二引数で指定可能
const arr = [1,2,3,4,5,6,7,8,9,10];

// 合計値を求める
const sum = arr.reduce((a,x) => a += x,0);
console.log(sum);  // 55

// 平均値を求める
const ave= arr.reduce((a,x) => a += x,0)/arr.length;
console.log(ave);  // 5.5

// 新しい配列を生成する
const arr2= arr.reduce((a,x) => a.concat(x+10),[]); // ※
console.log(arr2); // [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

※ concatは新しい配列を返します。Pushは新しい配列の要素数を返します。

配列関連のメソッドと削除された要素、定義されていない要素

map,filter,reduceの注意点

  • 未定義の要素に対しては関数が呼び出されない
  • 削除済みの要素に対しては関数が呼び出されない
const arr = Array(5).map(function(x){return 5});
console.log(arr);               // [empty × 5]

const arr2 = [1,2,3,4,5];
delete arr2[2];
const result = arr2.map(x => 0);
console.log(result);            // [0,0,,0,0]

join(配列の要素を一つの文字列に連結する)

メソッド join
内容 配列の要素を一つの文字列に連結する
変換方法 関数内で指定
レスポンス 任意の値が返却される
補足 区切り文字は第一引数で指定する
(未指定(=デフォルト)の区切り文字は,
nullundefinedは空文字になる
const arr = [1,null,3,undefined,5,true,7,false,9,"Hello","World"];
let result = arr.join();
console.log(result);            // 1,,3,,5,true,7,false,9,Hello,World
result = arr.join("");
console.log(result);            // 135true7false9HelloWorld
result = arr.join(" ");
console.log(result);            // 1  3  5 true 7 false 9 Hello World
result = arr.join("--");
console.log(result);            // 1----3----5--true--7--false--9--Hello--World

参考文献

moment().isValid()がバグってる!?

f:id:overworker:20200406080809j:plain:h250

moment().isValid()の振る舞いで納得できない点があるので少し掘り下げてみました。

24時00分00秒がtrue!?に納得できない。

const d1 = moment(`2020-07-02 24:00:00`);
console.log(d1.isValid());                            // true

const d2 = moment(`2020-07-03 00:00:00`);
console.log(d2.isValid());                            // true

両方ともtrueになるのは不自然ではないでしょうか?

やったこと

その1:厳密比較(があるらしい)→ 試してみた→ 変化なし

1-1:リファレンスをよく読む

以下、リファレンスの該当箇所です。

f:id:overworker:20200708230631p:plain

バージョン2.3.0以降では、最後の引数にブール値を指定して、モーメントで厳密な解析を使用できます。 厳密な解析では、フォーマットと入力がデリミタを含めて正確に一致する必要があります。

1-2:厳密比較を試してみた

// ----------------------------
//  24時00分00秒の振る舞い
// ----------------------------

// 厳密比較ではないので想定外とは言えない
console.log(moment('2020-07-02 24:00:00').isValid());                            //true

// 厳密比較ではないので想定外とは言えない
console.log(moment('2020-07-02 24:00:00','YYYY-MM-DD HH:mm:ss').isValid());      //true

// HH(=00~23)を想定している厳密比較なのでtrueは想定外
console.log(moment('2020-07-02 24:00:00','YYYY-MM-DD HH:mm:ss',true).isValid()); //true

// hh(=01~12)を想定している厳密比較なのでfalseは想定内
console.log(moment('2020-07-02 24:00:00','YYYY-MM-DD hh:mm:ss',true).isValid()); //false

// kk(=01~24)を想定している厳密比較なのでtrueは想定内
console.log(moment('2020-07-02 24:00:00','YYYY-MM-DD kk:mm:ss',true).isValid()); //true

// ----------------------------
//  その他のfalseの確認
// ----------------------------
console.log(moment('2020-07-02 24:00:01','YYYY-MM-DD HH:mm:ss',true).isValid()); //false
console.log(moment('2020-07-02 24:01:00','YYYY-MM-DD HH:mm:ss',true).isValid()); //false
console.log(moment('2020-07-02 23:58:60','YYYY-MM-DD HH:mm:ss',true).isValid()); //false
console.log(moment('2020-07-02 23:59:60','YYYY-MM-DD HH:mm:ss',true).isValid()); //false
console.log(moment('2020-07-02 22:60:00','YYYY-MM-DD HH:mm:ss',true).isValid()); //false
console.log(moment('2020-07-02 23:60:00','YYYY-MM-DD HH:mm:ss',true).isValid()); //false
console.log(moment('2020-07-02 23:60:60','YYYY-MM-DD HH:mm:ss',true).isValid()); //false
  • これでも不自然でした。

その2:問い合わせてみる

リファレンスサイト上から問い合わせる

問い合わせに関して、以下に記述がありました。

f:id:overworker:20200708235411p:plain

何か困ったことがあったら、まずガイドをチェックしましょう。

探しているものが見つからない場合は、スタックオーバーフローでmomentjsタグを使用して質問してみてください。

注:Stack Overflowで見られる問題の半分以上は、このブログ投稿で回答できます。

GitHubの課題追跡を使用して、関連する問題を見つけたり、新しい問題を開いたりすることもできます。

さらに、モーメントには内部チームが頻繁に利用できるGitterがあります。

一般的なトラブルシューティングのヘルプでは、スタックオーバーフローが推奨されるフォーラムです。 Momentのメンテナーは、他の知識のある他のユーザーと同様に、Stack Overflowに非常に積極的です。 最速の応答があります。
  • スタックオーバーフローでmomentjsタグを使用して質問してみてください。とあるのでstackoverflowに投稿することにしました。

stackoverflow(BETA)で問い合わせる

本家!?stackoverflowで問い合わせる

わかったこと

  • ISO 8601によって 24:00(:00) は midnight の表現として妥当である、と定められている
  • gitHubに公開されているmoment.jsのコード上に上記処理が記述されている

結論

  • 今のところは「そういうもの(仕様)」と割り切るしかないのかな?

JavaScriptで配列から重複する要素を取り除く

f:id:overworker:20200406080809j:plain:h250

方法1:filterメソッドとindexOfメソッドを利用する

const list = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'Sun', 'Sun'];
const day = list.filter((x,i,self) =>{
  return self.indexOf(x) === i;
})
console.log(day);               // ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
  • Array.prototype.indexOf()
    • indexOf() メソッドは引数に与えられた内容と同じ内容を持つ配列要素の内、最初のものの添字を返します。存在しない場合は -1 を返します。

方法2:スプレッド構文とSetオブジェクトを利用する

const list = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'Sun', 'Sun'];
const day = [...(new Set(list))];
console.log(day);               // ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
  • Setオブジェクトは重複しない値の集合を管理するためのオブジェクト。

その他の記事について

overworker.hatenablog.jp