配列処理(some,everyメソッド)

はじめに

今回の記事は配列の便利メソッドであるsomeメソッド、everyメソッドについてTypeScriptのコードで以下記事を書いていきます。

someメソッド、everyメソッドの基本

someメソッド(everyメソッドも同様)の引数であるコールパック関数の引数は、mapメソッド同様最大3つの引数を設定できます。
第1引数は、配列の一つ一つの要素。第2引数は、index(0からの添字)。第3引数は配列自体。第2,3引数はめったに使われないと思います。

someメソッドの役割は、「与えた条件を満たす要素がひとつでもあるか」を真偽値で返すことにあります。

またeveryメソッドの役割は「与えた条件をすべての要素が満たすか」を真偽値で返すことにあります。

ポイントはどちらもBooleanが返り値ということです。

以下で確認しましょう。

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

const reultOfSome = arr.some((num) => num !== 0);

const resultOFEvery = arr.every((num) => num >= 2);

console.log(reultOfSome); //true
console.log(resultOFEvery); //false

some,everyの例題

①アンケートを実施した結果がusersという連想配列に格納されています。ユーザー全員が回答済みかどうかを確認し、hasSubmitted変数に結果(trueかfalse)を格納しなさい。

type Users = {
  id: number;
  hasSubmitted: Boolean;
}[];

const users: Users = [
  { id: 2, hasSubmitted: true },
  { id: 3, hasSubmitted: false },
  { id: 4, hasSubmitted: true },
];

解答

const hasSubmitted = users.every(({ hasSubmitted }) => hasSubmitted);

配列処理(findメソッド)

はじめに

今回の記事は配列の便利メソッドの一つfindメソッドについてTypeScriptのコードで以下記事を書いていきます。

findメソッドの基本

find内コールパック関数の引数は、mapメソッド同様最大3つの引数を設定できます。
第1引数は、配列の一つ一つの要素。第2引数は、index(0からの添字)。第3引数は配列自体。第2,3引数はめったに使われないと思います。

またfindメソッドの役割としては、与えた条件に適応する最初の要素を返すことにあります。(条件に合わなければundefinedを返す)
以下コードで確認しましょう。

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

const result1 = arr.find((value) => value > 4);
const result2 = arr.find((value) => value === 4);
console.log(result1);//5
console.log(result2);//4

findメソッドを用いた例題

①以下の連想配列(users)の中から、管理者権限(admin)を持っている(true)ユーザーを探し、そのユーザーをadminという変数に格納しなさい。

type Users = {
  id: number;
  admin: Boolean;
}[];

const users: Users = [
  { id: 1, admin: false },
  { id: 2, admin: true },
  { id: 3, admin: false },
  { id: 4, admin: true },
  
];

解答

const admin = users.find(({ admin }) => admin);

該当する項目が複数ある場合でも、先頭から探して最初に見つけたものだけが返る点に注意して下さい。全て欲しい場合は filterメソッドを使います。
配列処理(filterメソッド) - 駆け足エンジニアの記録



②次の連想配列(member)の中から,田中さんのオブジェクトを抽出するfindTanakaという関数を作成しなさい。

type Members = {
  name: string;
  age: number;
  sex: "male" | "female";
}[];

const members: Members = [
  { name: "松井", age: 39, sex: "male" },
  { name: "今田", age: 34, sex: "female" },
  { name: "鈴木", age: 24, sex: "male" },
  { name: "山田", age: 56, sex: "male" },
  { name: "田中", age: 89, sex: "female" },
];

解答

const findTanaka = (array: Members) => {
  return array.find(({ name }) => name === "田中");
};
console.log(findTanaka(members));

findIndexメソッド

findメソッドから派生したものとしてfindIndexメソッドというものがあります。
findIndexメソッドの役割は、与えた条件に適合した最初の要素のインデックスを返すことにあります。(条件に合わなければ-1を返す)

以下のコードで確認しましょう。

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

const result = arr.findIndex((num) => num === 4);
console.log(result); //3

このような用途であればindexOfで書き換え可能です。

const result1 = arr.indexOf(4);
console.log(result1); //3

ただしindexOfメソッドは引数に配列の要素しか代入できないため用途が限られます。
findIndexメソッドであれば引数がコールバック関数のため比較演算子などを用いて要素のインデックスを返すことができるため,(配列処理においては)findIndexメソッドはindexOfメソッドの上位互換といえます。

const result = arr.findIndex((num) => num > 4);
console.log(result); //4

最後に連想配列についてfindIndexメソッドを利用した配列処理をコードで確認しましょう。

type Members = {
  name: string;
  age: number;
  sex: "male" | "female";
}[];

const members: Members = [
  { name: "松井", age: 39, sex: "male" },
  { name: "今田", age: 34, sex: "female" },
  { name: "鈴木", age: 24, sex: "male" },
  { name: "山田", age: 56, sex: "male" },
  { name: "田中", age: 89, sex: "female" },
];

const index2 = members.findIndex(({ name }) => name === "鈴木");
console.log(index2); //2

配列処理(filterメソッド)

はじめに

今回の記事は配列の便利メソッドfilterの記事になります。mapメソッドと絡めた問題も用意していますので、mapメソッドについて理解が浅い方はこちらの記事を参照して下さい。

fiterメソッドの基本
fiter内コールパック関数の引数は、mapメソッド同様最大3つの引数を設定できます。
第1引数は、配列の一つ一つの要素。第2引数は、index(0からの添字)。第3引数は配列自体。第3引数はめったに使われないと思います。

mapメソッドとの使い分けですが、fiterメソッドは(if文、比較演算子等で)与えた条件に適合する要素だけを抽出した新しい配列を返すといった手段で活用されます。

以下の例で確認しましょう。

const ary = [1, 2, 3, 4, 5];

const result1 = ary.filter((value) => {
  return value > 3;
});

const result2 = ary.filter((_, index) => {
  return index > 3;
});
console.log(result1);//[4,5]
console.log(result2);//[5]

fiterメソッドを用いた例題

①以下の連想配列(users)の中から、管理者権限(admin)を持っている(true)ユーザーに絞り込み、filteredUsersという変数に格納しなさい。

type Users = {
  id: number;
  admin: Boolean;
}[];

const users: Users = [
  { id: 1, admin: true },
  { id: 2, admin: true },
  { id: 3, admin: false },
  { id: 4, admin: true },
  { id: 5, admin: false },
];

解答

const filteredUsers = users.filter(({ admin }) => admin);

②次の連想配列(member)の中から35歳以上の名前(name)の値だけを抜き取った配列が返るような関数getNameOver35を作成しなさい。

type Members = {
  name: string;
  age: number;
  sex: "male" | "female";
}[];

const members: Members = [
  { name: "松井", age: 39, sex: "male" },
  { name: "今田", age: 34, sex: "female" },
  { name: "鈴木", age: 24, sex: "male" },
  { name: "山田", age: 56, sex: "male" },
  { name: "田中", age: 89, sex: "female" },
];

解答

type GetNameOver35<T extends Members, U> = (array: T) => U[];

const getNameOver35: GetNameOver35<Members, string> = (array) => {
  return array.filter(({ age }) => age > 35).map(({ name }) => name);
};
console.log(getNameOver35(members));

③以下のような重複値を含む配列arrの中から、重複値を除く互いに素な配列を作成しなさい。

const arr = [2, 4, 7, 5, 4];

解答

const coprimeArr = arr.filter((num, i, arrr) => {
  return arrr.indexOf(num) === i;
});
console.log(coprimeArr);//[2, 4, 7, 5]

④次の連想配列の中からnameプロパティをもったユーザーに絞り込み、getNameという変数に格納しなさいさい。

interface User {
  id: number;
  name?: string;
}

const users: User[] = [
  { id: 1, name: "豊臣" },
  { id: 2 },
  { id: 3, name: "織田" },
];

解答

const getName = users
  .filter((user: User) => "name" in user)
  .map((user) => user);

配列処理(mapメソッド)

はじめに

今後しばらくの間、配列処理のメソッドについての記事を残します。(以降掲載予定のメソッドはfilter,find,every,some,reduce,includes)
またこれらのメソッドはforEachで書き換え可能ですが、forEachは実行するだけで値を返さないというデメリット(デメリットと感じるのは僕個人の見解です)を持っています。
なので基本的には上記の配列処理メソッドに頼って、どうしてもという時にforEachを使うといった方針で配列を処理しようと思います。

mapの基礎構文

map内コールバック関数の引数は最大3つの引数を設定できます。
第1引数は、配列の一つ一つの要素。第2引数は、index(0からの添字)。第3引数は配列自体。
第1引数はよく使いますが第2、第3引数はあまり使わないといった印象です。

const data = [2, 4, 6, 8];
const result1 = data.map((value) => {
  return value * value;
});
const result2 = data.map((_, index) => {
  return index;
});
const result3 = data.map((_, __, array) => {
  return array;
});
console.log(result1);//[4, 16, 36, 64]
console.log(result2);//[0, 1, 2, 3]
console.log(result3);
//0: (4) [2, 4, 6, 8]
//1: (4) [2, 4, 6, 8]
//2: (4) [2, 4, 6, 8]
//3: (4) [2, 4, 6, 8]

mapメソッドを用いた例題

①次の連想配列(images)のheightだけを取得し、新しい配列(heights)を作成しなさい。

type Images = {
  height: string;
  width: string;
}[];

const images: Images = [
  { height: "20px", width: "40px" },
  { height: "34px", width: "56px" },
  { height: "28px", width: "64px" },
];
解答
const heights = images.map(({ height }) => {//オブジェクトの分割代入の利用
  return height;
});


②次の連想配列(member)の中から名前(name)の値だけを抜き取った配列が返るような関数getNameを作成しなさい。

type Members = {
  name: string;
  age: number;
  sex: "male" | "female";
}[];

const members: Members = [
  { name: "松井", age: 39, sex: "male" },
  { name: "今田", age: 34, sex: "female" },
  { name: "鈴木", age: 24, sex: "male" },
  { name: "山田", age: 56, sex: "male" },
  { name: "田中", age: 89, sex: "female" },
];
解答
type GetName<T extends Members, U> = (array: T) => U[];

const getName: GetName<Members, string> = (array) => {
  return array.map(({ name }) => name);
};
console.log(getName(members)); //["松井", "今田", "鈴木", "山田", "田中"]

JavaScript(オブジェクトの反復処理)

はじめに

オブジェクトの反復処理の一つにfor...in文がありますが、Airbnb のスタイルガイドでは推奨されていません。その代用手段として、オブジェクト自身が持つメソッドを使っていったん配列を形成する方法が推奨されています。
GitHub - airbnb/javascript: JavaScript Style Guide
今回の記事はオブジェクトから配列に変換するためのメソッド、また例題を以下で紹介します。

三大配列に変換するオブジェクトメソッド

以下、コードで確認しましょう。

const person = {
  name: "Naruto",
  age: 17,
  gender: "male",
};
Object.keys(person);//["name", "age", "gender"]
Object.values(person);//["Naruto", 17, "male"]
Object.entries(person);//(3) [Array(2), Array(2), Array(2)]
//0: (2) ["name", "Naruto"]
//1: (2) ["age", 17]
//2: (2) ["gender", "male"]

keysメソッドはオブジェクトのプロパティ名、valuesメソッドはオブジェクトの値をそれぞれ配列に変換します。3つ目のentriesメソッドはオブジェクトのプロパティとその値のペアを二次元配列に変換します。

また勉強の一つとして、keyメソッドを使ってvalueメソッド、entriesメソッドを実装した例が以下のとおりです。(オブジェクトは上のコードと同じです。)

//valueメソッドの実装
Object.keys(person).map((prop) => {
  return person[prop]
});

//entriesメソッドの実装
Object.keys(person).map((prop) => {
  return [prop, person[prop]];
});

Array.from()メソッド

Array.from() メソッドは、配列風オブジェクトや反復可能オブジェクトから、新しい、浅いコピーの Array インスタンスを生成します。(MDNより引用)
Array.from() - JavaScript | MDN


純粋なオブジェクトをArray.fromメソッドで配列に変換することはできません。

const person = {
  name: "Naruto",
  age: 17,
  gender: "male",
};
const arry = Array.from(person);
console.log(arry);//[]
配列風オブジェクト

配列風オブジェクトとはlengthプロパティを持っており [0], [1], [2], … で要素にアクセスできるオブジェクトを指します。数値のプロパティは0から(index順なので注意して下さい。)
以下、配列風オブジェクトにArray.fromメソッドを利用した例です。

const person = {
  0: "Naruto",
  1: 17,
  2: "male",
  length: 3,
};
const arry = Array.from(person);
console.log(arry);//["Naruto", 17, "male"]

またArray.fromメソッドは第2引数にコールパック関数を取ります。このコールパック関数は配列の便利メソッドmap同様の挙動を取ります。

const person = {
  0: "Naruto",
  1: 17,
  2: "male",
  length: 3,
};
const arry = Array.from(person, (_,i) =>i+1);
console.log(arry);//[1,2,3]
反復可能なオブジェクト

反復可能な(iterables) オブジェクトは配列の汎化です。for...ofで反復処理を実行できるオブジェクトは反復可能なオブジェクトとなります。具体的な例を挙げると、配列、文字列、MapやSetが該当します。
ここではMap(反復可能なオブジェクト)にArray.fromメソッドを利用します。

const map = new Map([
  ["name", "Naruto"],
  ["age", 17],
  ["gender", "male"],
]);
const arry = Array.from(map);
console.log(arry);//[Array(2), Array(2), Array(2)]
//0: (2) ["name", "Naruto"]
//1: (2) ["age", 17]
//2: (2) ["gender", "male"]

このようにオブジェクトメソッドのentriesメソッド同様の結果を返します。

練習問題

最後に以上の内容を踏まえた問題を作りました。(後日追加予定です。)

①1から100まで格納された配列
const arry = [...new Array(101 - 1).keys()].map((n) => n + 1);

//別解
const array = Array.from({ length: 100 }, (_, i) => i + 1);

Nullish Coalescing と Optional Chaining

はじめに

今回の記事はjavascript,typescriptどちらでも利用可能であるNull 合体演算子(Nullish Coalescing)とオプショナルチェイン(Optional Chaining)についての記事になります。以下、Typescriptのコードで理解を深めましょう。

Optional Chainingの基本

interface User {
  name: string;
  age: number;
  address?: {//←ここでの?は省略可能を表す
    town: string;
  };
}

const user1: User = {
  name: "max",
  age: 22,
  address: {
    town: "Maple Town",
  },
};
const user2: User = {
  name: "Green",
  age: 19,
};
console.log(user1?.address?.town); //Maple Town
console.log(user2?.address?.town); //undifined

ここでは最後の?と.(ドット)で繋いでいるところに注目して下さい。これがOptional Chainingです。?. 演算子を使うと、チェーン内の各参照が正しいかどうかを明示的に確認せずにアクセスしていくことができます。途中のプロパティが存在していなかったら、(エラーを出さず)user2のように式が短絡されて undefined を返してくれます。
ちなみにチェーンできるのはプロパティだけじゃなくメソッドの場合でも可能です。

Nullish Coalescingの基本

以下、先程のコードを少しいじりました。

interface User {
  name: string;
  age: number;
  address?: {
    town: string;
  };
}

const user1: User = {
  name: "max",
  age: 22,
  address: {
    town: "Maple Town",
  },
};
const user2: User = {
  name: "Green",
  age: 19,
};

const town1 = user1.address?.town ?? "Tokiwa Town";
const town2 = user2.address?.town ?? "Tokiwa Town";
console.log(town1); //Maple Town
console.log(town2); //Tokiwa Town

上記のtown1やtown2で使用している ??、これが nullish coalescing です。これは左辺がnull または undefinedのときだけ右辺(Tokiwa Town) が評価されます。

このケースではNullish Coalescing(??)を次のように書き直すことが可能です。

const town1 = user1.address?.town || "Tokiwa Town";
const town2 = user2.address?.town || "Tokiwa Town";
console.log(town1); //Maple Town
console.log(town2); //Tokiwa Town

OR 演算子(||)に書き換えました。

??と||の違いは、??は左辺がnull または undefinedのときだけ右辺(Tokiwa Town) が評価されるのに対し、||では左辺がfalsyな値(0,'',null,undefinedなど)であれば右辺が評価されるという違いがあります。つまり||では 0 や空文字もスルーされてし まうので、より明示的な nullish coalescing のほうが望ましというわけです。


Optional ChainingとNullish Coalescing
最後に.?と??を組み合わせたコードを見てこの記事を終わりとします。

const pirate = [
  {
    name: "Luffy",

    address: {
      town: "Dawn Island",
    },
  },

  {
    name: "Zoro",

    address: {},
  },

  null,
];

for (let u of pirate) {
  const user = u ?? { name: "(Somebody)" };

  const town = u?.address?.town ?? "(Somewhere)";

  console.log(`${user.name} lives in ${town}`);
}
//Luffy lives in Dawn Island
//Zoro lives in (Somewhere)
//(Somebody) lives in (Somewhere)

Typescript(条件型)

はじめに

今回の記事は型の三項演算子である条件型(Conditional Types)についての記事になります。因みに、Conditionalとは条件次第の、条件を含む、条件付きのと解釈されます。

条件型の基礎

はじめに述べたとおり条件型とはまさに型の三項演算子です。(大切なことなので二度言いました...)

type MyCondition<T, U, X, Y> = T extends U ? X : Y;

ここでは4つの型が存在し、MyConditionは「TがUのサブクラス(部分型)ならばXを、そうでなければY」という型を表現します。
利用方法は以下のとおりです。

type MyCondition<T, U, X, Y> = T extends U ? X : Y;

type A = MyCondition<string, string, true, false>; //true
type B = MyCondition<number, string, true, false>; //false

ただ実際にはこのようなジェネリック型パラメータを宣言するための山括弧<>の数が4つの型(T,U,X,Y)である抽象的な型定義を使用するケースはないように思えます。
以下のような使い方でしょうか。

type IsString<T> = T extends string ? true : false;
//Uはstring、Xはtrue、Yはfalse
type A = IsString<string>; //true
type B = IsString<number>; //false


また条件型をベースとした分配条件型というものが存在します。

type IsString<T> = T extends string ? true : false;

①type A = IsString<"a" | "A">; //true
②type B = IsString<1 | 2>; //false
③type C = IsString<"a" | 1>; //boolean

①~③に共通していることは山括弧の中身がユニオン型で表されています。
その上でそれぞれを解説すると、
①では'a','A'ともにstring型のサブクラスであるためtrueが返ります。
②は1,2はどちらもstring型のサブクラスでないためfalseが返ります。
③は'a'がstring型のサブクラスですが(この時点でtrueが返る)、1がstrung型のサブクラスでない(ここでfalseが返る)ため最終的にはboolean(true|false)が返ります。

条件型の応用

inferキーワード

inferキーワードとは条件の一部としてジェネリクス型を宣言できる機能です。因みに、inferとは本来Type inference in conditional types の略で条件型における型推論と解釈できます。

一つ例を提示します。

class American {
  constructor(
    public name: string,
    public age: number,
    public PostalCode: number
  ) {}
}
class Japanese {
  constructor(
    public name: string,
    public age: number,
    public PostalCode: string
  ) {}
}

アメリカ在住、日本在住という感じで2つの簡単なクラスを作成しました。2つのクラスのうち、PostalCode(郵便番号)プロパティのみ型が異なります。この後、アメリカ在住、日本在住の人の条件にあったコードを組みたときなどにPostalCodeの型によって違いを出せそうです。
そのような時にinferが使えそうです。

type PostalCode<T> = T extends { PostalCode: infer U } ? U : never;
type EnPostalCode = PostalCode<American>; //型はnumber
type JaPostalCode = PostalCode<Japanese>; //型はstring

PostalCodeという型エイリアスにはジェネリクス型を用いています。条件型にはジェネリクス型をインラインで宣言する(ここではそれがUにあたる)ための独自の構文があります。それがinferキーワードです。PostalCodeではT型にクラス名が入りそのクラスから推測してPostalCodeの型U(numberかstring)を決定すると表現されています。

郵便番号がない国ウガンダ在住のクラスに対しては、決して戻ることのない型neverとなります。

class Ugandan {
  constructor(public name: string, public age: number) {}
}
type PostalCode<T> = T extends { PostalCode: infer U } ? U : never;
type UgPostalCode = PostalCode<Ugandan>; //型はnever