📝

配列の問題集で勉強したら、配列が少し扱えるようになった

2022/07/01に公開2

Tsuboiさんという方が、ZennでJavaScript(TypeScriptを含む)の問題集を公開している。
https://zenn.dev/tsuboi/articles/c4f17185a6181f013108

配列の扱いについて苦手意識を持っていたので、勉強するのにちょうど良いと思い、Tsuboiさんの問題集で勉強してみた。

自分なりに解いた問題もあれば、わからなくて解答を見ながら調べて理解を深めた問題もある。

どのように理解をしたのかをメモしながら進めたので、誰かの参考になるかもしれないと思い、ブログで公開することにした。

問題1

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題①

[ …new Array(100).keys() ]で、100個の要素を持つ配列を作ることができ、要素の中身は0から始まる正の整数が連番になっている。

なので、0〜99の連番の配列が作れていることになる。

全要素に1を足したいので、mapで繰り返し処理をして、全要素に1を足していき、新しい配列を作ることで、1から100までの数字が格納された配列が出来上がる。

const arry = [...new Array(100).keys()].map((n) => n + 1);

問題2

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題②

typeを使うことで独自の型を定義できるんだけど、ここではImagesという型が定義されている。

オブジェクトの中身に、string型のheightとstring型のwidthというキーを持つオブジェクトで、さらに最後に配列を表す[ ]がついているので、Imagesという型は配列であり、配列の各要素は指定されたキーを持つオブジェクトになる。

Images型のimagesという配列が定義されている。

配列imagesの中の要素を操作して、heightの値だけの配列を生成する。

配列の要素を操作するにはmapを使えばよい。

mapは引数にコールバック関数を渡すことができて、コールバック関数の引数にはmapを使っている配列の各要素が1つずつ渡される。なので、1つ目は{ height: "20px", width: "40px" }が渡される。ここからheightだけの配列を作りたいので分割代入でheightだけ引っこ抜く。{ height } = { height: "20px", width: "40px" }という状態になって、キーがheightである”20px”だけが代入される。代入された文字列をそのまま返すことで、新しい配列の1つ目の要素は”20px”になる。これを全要素繰り返し、新しい配列が生成される。

images.map(({height}) => height);
// ["20px", "34px", "28px"]

問題3

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題③

typeを使うことで独自の型を定義できるんだけど、ここではMembersという型が定義されている。

オブジェクトの中身に、string型のnameとnumber型のageとmaleかfemaleという文字列しか持たないgenderというキーを持つオブジェクトで、さらに最後に配列を表す[ ]がついているので、Membersという型は配列であり、配列の各要素は指定されたキーを持つオブジェクトになる。

Members型のmembersという配列が定義されている。

配列membersの中の要素を操作して、nameの値だけの配列を生成する。

配列の要素を操作するにはmapを使えばよい。

mapは引数にコールバック関数を渡すことができて、コールバック関数の引数にはmapを使っている配列の各要素が1つずつ渡される。なので、1つ目は{ name: "松井", age: 39, gender: "male" }が渡される。ここからnameだけの配列を作りたいので分割代入でnameだけ引っこ抜く。{ name } = { name: "松井", age: 39, gender: "male" }という状態になって、キーがnameである”松井”だけが代入される。代入された文字列をそのまま返すことで、新しい配列の1つ目の要素は”松井”になる。これを全要素繰り返し、新しい配列が生成される。

members.map(({name}) => name);
// ["松井", "今田", "鈴木", "山田", "田中"]

この問題のゴールは、上記のような処理ができる関数を作ることなので、関数を定義する。

TypeScriptで関数を定義する時は、引数の型と返り値の型を指定する。

今回は、引数にMembers型の配列、返り値に文字列型の配列を指定したいのでこうすることができそう。

const getName = (array: Members): string[] => {
	
}

中身はさっきの処理を流用する。

const getNameOver35 = (array: Members): string[] => {
	return array.map(({name}) => name);
}

また、引数と返り値の型を固定せずに、その時々で変更させることも可能。

記事にある解答を例として考えてみる。

事前にtypeで独自の型を定義し、ジェネリクスを使うと実現できる。ジェネリクスは、型指定を動的にできるもので、仮の引数を用意して、あとでそこに型を代入する。

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

const getName<Members, string> = (array) => {
	return array.map(({name}) => name);
}

ジェネリクスでは、仮引数は別になんでもいいんだけど、TやUが使われることが多い。TにはMembers、Uにはstringが入ることになるので、

type GetName<Members extends Members, string> = (array: Members) => string[];

のような状態になる。

問題4

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題④

絞り込む時はfilterを使う。

filterは配列の各要素から条件に合った要素だけに絞り込むことができる。

filterの引数にはコールバック関数を渡すんだけど、コールバック関数の引数には、filterをかける配列の各要素が1つずつ渡されてくる。なので、1つ目に渡されるのは{ id: 1, admin: true }。管理者権限を持っているかどうかの条件に使いたいのはadminだけなので、分割代入を使うことでオブジェクトから必要な要素だけを引っこ抜く。{ admin } = { id: 1, admin: true }という状態になって、キーがadminであるtureだけが代入される。これを全要素繰り返し、新しい配列が生成される。

users.filter(({admin}) => admin);
// [
//  { id: 1, admin: true },
//  { id: 2, admin: true },
//  { id: 4, admin: true }
// ]

問題5

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑤

絞り込むのではなく、配列自体を操作して新しい配列を生成する時はmapを使う。

mapは引数にコールバック関数を渡すことができて、コールバック関数の引数にはmapを使っている配列の各要素が1つずつ渡される。なので、1つ目は["Ruffy", "captain"]が渡される。ここからindex番号0の要素だけを返したいので、["Ruffy", "captain"][0]として、”Ruffy”だけを返すことで、新しい配列の1つ目の要素は”Ruffy”になる。これを全要素繰り返し、新しい配列が生成される。

array.map((element) => {
	return element[0];
});
// ["Ruffy", "Zoro"]

問題6

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑥

絞り込む時は、filterを使う。

filterは配列の各要素から条件に合った要素だけに絞り込むことができる。

filterの引数にはコールバック関数を渡すんだけど、コールバック関数の第1引数には、filterをかける配列の各要素が1つずつ渡されてきて、第2引数には、index番号が渡される。

index番号が0のものだけに絞り込むので、以下のようになる。

array.filter((element, index) => {
	return index === 0;
});
// ["Ruffy", "captain"]

引用元では、index === 0 && keyとなっていて、たぶん意味があると思うけどわからなかった。もしわかる方いたら教えていただきたいです。

問題7

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑦

typeを使うことで独自の型を定義できるんだけど、ここではMembersという型が定義されている。

オブジェクトの中身に、string型のnameとnumber型のageとmaleかfemaleという文字列しか持たないgenderというキーを持つオブジェクトで、さらに最後に配列を表す[ ]がついているので、Membersという型は配列であり、配列の各要素は指定されたキーを持つオブジェクトになる。

Members型のmembersという配列が定義されている。

まず、ここから35歳以上の要素だけに絞り込む。

絞り込む時はfilterを使う。

filterは配列の各要素から条件に合った要素だけに絞り込むことができる。

filterの引数にはコールバック関数を渡すんだけど、コールバック関数の引数には、filterをかける配列の各要素が1つずつ渡されてくる。なので、1つ目に渡されるのは{ name: "松井", age: 39, gender: "male" }。年齢の条件に使いたいのはageだけなので、分割代入を使うことでオブジェクトから必要な要素だけを引っこ抜く。{ age } = { name: "松井", age: 39, gender: "male" }という状態になって、キーがageである39だけが代入される。これを全要素繰り返し、新しい配列が生成される。

members.filter(({age}) ⇒ age ≥ 35 );

filter後には、こうなっている。

[
  { name: "松井", age: 39, gender: "male" },
  { name: "山田", age: 56, gender: "male" },
  { name: "田中", age: 89, gender: "female" },
]

ここからnameだけを抜き取った配列にするには、各要素を操作する必要があって、各要素を操作する時はmapを使う。

mapも引数にコールバック関数を渡すことができて、コールバック関数の引数にはmapを使っている配列の各要素が1つずつ渡される。なので、1つ目は{ name: "松井", age: 39, gender: "male" }が渡される。ここからnameだけを条件として使いたいので分割代入でnameだけ引っこ抜く。{ name } = { name: "松井", age: 39, gender: "male" }という状態になって、キーがnameである”松井”だけが代入される。代入された文字列をそのまま返すことで、新しい配列の1つ目の要素は”松井”になる。これを全要素繰り返し、新しい配列が生成される。

members.filter(({age})) => age >= 35 ).map(({name}) => name );

filterとmap後には、こうなっている。

["松井", "山田", "田中"]

この問題のゴールは、上記のような処理ができる関数を作ることなので、関数を定義する。

TypeScriptで関数を定義する時は、引数の型と返り値の型を指定する。

今回は、引数にMembers型の配列、返り値に文字列型の配列を指定したいのでこうすることができそう。

const getNameOver35 = (array: Members): string[] => {
			
}

中身はさっきの処理を流用する。

const getNameOver35 = (array: Members): string[] => {
	return array.filter(({age}) => age >= 35).map(({name}) => name);
}

また、引数と返り値の型を固定せずに、その時々で変更させることも可能。

記事にある解答を例として考えてみる。

事前にtypeで独自の型を定義し、ジェネリクスを使うと実現できる。ジェネリクスは、型指定を動的にできるもので、仮の引数を用意して、あとでそこに型を代入する。

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

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

ジェネリクスでは、仮引数は別になんでもいいんだけど、TやUが使われることが多い。TにはMembers、Uにはstringが入ることになるので、

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

のような状態になる。

問題8

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑧

■重複値を削除したい時は、Setオブジェクトを使う

重複値を削除したい時は、Setオブジェクトを使うとシンプルに書ける。

new Set()の引数に配列を渡すと、配列をSetオブジェクトに変換して、渡した配列の重複値を削除することができる。

new Set(arr);
// Set {2, 4, 7, 5}

でも、Setオブジェクトは配列ではない。最終的に配列を作りたいなら、Setオブジェクトを配列に変換してあげる必要がある。

Setオブジェクトを配列に変換する方法は2種類ある

  • Array.from(new Set(array))
  • […new Set(array)]

■Array.from(new Set(array))

Array.from(new Set(arr));
// [2, 4, 7, 5]

Array.fromは配列風オブジェクトや反復可能オブジェクトから配列を作ることができる。

Setオブジェクトは反復可能オブジェクトなので、Array.fromで配列に変換することができる。

ちなみに、配列風オブジェクトと反復可能オブジェクトは違うもの。

https://ja.javascript.info/iterable#ref-2520

Setオブジェクトにはlengthはない。でも、for…ofで反復処理が可能だから反復可能オブジェクトにあたる。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/for...of

■[…new Set(array)]

[...new Set(arr)];
// [2, 4, 7, 5]

スプレッド構文でSetオブジェクトから配列に変換することができる。

スプレッド構文は、[ ]や{ }を外してくれる。

https://techtechmedia.com/spread-syntax-javascript/#rtoc-2

Setオブジェクトにスプレッド構文を使うと{ }を外すことができて、それを[ ]で囲めば配列に変換できる。

[...new Set(arr)];[...{2, 4, 7, 5}];[2, 4, 7, 5]

問題9

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑨

独自の型を定義する方法は、type以外にもinterfaceを使う方法がある。

ここでは、number型のidとstring型のnameというキーを持ったオブジェクトであることを宣言している。nameには?がついているので必須ではない。

また、interfaceは、オブジェクトと関数の型のみ宣言することができる。なので、typeのように配列であることまでは宣言できない。

なので、配列であること表すには、interfaceを使う時に[ ]をつける。

配列usersを定義する時に、User[]とすることで配列であり各要素はUser型のオブジェクトであることを表せる。

nameプロパティを持った要素だけに「絞り込む」のでfilterを使う。

filterは配列の各要素から条件に合った要素だけに絞り込むことができる。

filterの引数にはコールバック関数を渡すんだけど、コールバック関数の引数には、filterをかける配列の各要素が1つずつ渡されてくる。なので、1つ目に渡されるのは{ id: 1, name: "豊臣" }。条件に使いたいのはnameだけなので、分割代入を使うことでオブジェクトから必要な要素だけを引っこ抜く。{ name } = { id: 1, name: "豊臣" }という状態になって、キーがnameである”豊臣”だけが代入される。これを全要素繰り返し、新しい配列が生成される。

user.filter(({name}) => name);
// [
//  { id: 1, name: "豊臣" },
//  { id: 3, name: "織田" },
// ]

nameプロパティを持っていないときはfalseになって返されないので新しい配列には含まれない。

問題10

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑩

typeを使うことで独自の型を定義できるんだけど、ここではUsersという型が定義されている。

オブジェクトの中身に、number型のidとBoolean型のadminというプロパティを持つオブジェクトで、さらに最後に配列を表す[ ]がついているので、Usersという型は配列であり、配列の各要素は指定されたプロパティを持つオブジェクトになる。

Users型のusersという配列が定義されている。

ここからadmin: trueを持った要素だけに絞り込むんだけど、最初に見つけた要素だけという条件があるので、findを使う。filterは条件に合った要素すべてを抽出するのでここでは適さない。

findは配列の各要素から条件に合った1つ目の要素だけを抽出できる。

findの引数にはコールバック関数を渡す。コールバック関数の引数には、findを使う配列の各要素が1つずつ渡されてくる。なので、1つ目に渡されるのは{ id: 1, admin: false }。条件に使いたいのはadminだけなので、分割代入を使うことでオブジェクトから必要な要素だけを引っこ抜く。{ admin } = { id: 1, admin: false }という状態になって、キーがadminであるfalseだけが代入される。これを全要素繰り返し、新しい配列が生成される。

const admin = users.find(({admin}) => admin);
// console.log(admin); → { id: 2, admin: true }

問題11

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑪

田中が1人だけいる前提ならfind

田中が何人いるかわからない前提ならfilter

を使うとよさそう。

findは条件と一致した初めの要素を抽出する

filterは条件と一致した要素すべてを抽出する

findもfilterも、配列に対して使うことができて、引数にコールバック関数を渡せる。コールバック関数の引数には、配列の要素が順番に1つずつ入ってきて、コールバック関数の処理がtrueになった場合、返してくれて、返された要素が新しい配列の要素になる。

ここではコールバック関数の引数に分割代入を使用している。1周目に引数に入ってくる要素は { name: "松井", age: 39, gender: "male" }で、ここからnameだけ引っこ抜きたいときに、代入される側でプロパティ名を{ }で囲むことで、”松井”だけを引っこ抜ける。

引っこ抜いたものが絞り込みたい条件と一致するかを調べて、期待してる値を作り出す。

以下のような関数が答えになると思う。

const findTanaka = () => {
	members.find(({name}) => name === "田中");
}

or

const findTanaka = () => {
	members.filter(({name}) => name === "田中");
}

問題12

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑫

typeを使うことで独自の型を定義できるんだけど、ここではMembersという型が定義されている。

オブジェクトの中身に、number型のidとboolean型のhasSubmittedというプロパティを持つオブジェクトで、さらに最後に配列を表す[ ]がついているので、Usersという型は配列であり、配列の各要素は指定されたプロパティを持つオブジェクトになる。

Users型のusersという配列が定義されている。

すべての要素が条件を満たしているどうかを調べる時は、everyを使う。

everyは配列が条件をすべて満たす場合にtrueを返す。

everyの引数にはコールバック関数を渡すんだけど、コールバック関数の引数には、everyを使う配列の各要素が1つずつ渡されてくる。なので、1つ目に渡されるのは{ id: 2, hasSubmitted: true }。条件に使いたいのはhasSubmittedだけなので、分割代入を使うことでオブジェクトから必要な要素だけを引っこ抜く。{ hasSubmitted } = { id: 2, hasSubmitted: true }という状態になって、キーがhasSubmittedであるtrueだけが代入される。これを全要素繰り返し、新しい配列が生成される。

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

問題13

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑬

typeを使うことで独自の型を定義できるんだけど、ここではTripsという型が定義されている。

オブジェクトの中身に、number型のdistanceというプロパティを持つオブジェクトで、さらに最後に配列を表す[ ]がついているので、Tripsという型は配列であり、配列の各要素は指定されたキーを持つオブジェクトになる。

Trips型のtripsという配列が定義されている。

まず、各要素のキーだけを取り出した配列を生成する。

配列の要素を操作する時はmapを使う。

mapは引数にコールバック関数を渡すことができて、コールバック関数の引数にはmapを使っている配列の各要素が1つずつ渡される。なので、1つ目は{ distance: 34 }が渡される。ここからnameだけを条件として使いたいので分割代入でnameだけ引っこ抜く。{ distance } = { distance: 34 }という状態になって、キーがdistanceである34だけが代入される。代入された文字列をそのまま返すことで、新しい配列の1つ目の要素は34になる。これを全要素繰り返し、新しい配列が生成される。

次に、配列の各要素を合計する。

配列の各要素を合計するにはreduceを使う。

reduceは引数にコールバック関数を渡すことができて、コールバック関数の引数の第1引数には蓄積値、第2引数には配列の各要素が1つずつ渡される。ポイントは、蓄積値には初期値として配列の0番目の要素が入り、第2引数に1回目に渡される要素は1番目の要素であること。蓄積値と渡される配列の要素を足し合わせたものが次の蓄積値となる。

const totalDistance: number = trips.map(({distance}) => distance).reduce((hoge, hogehoge) => hoge + hogehoge);

問題14

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑭

typeを使うことで独自の型を定義できるんだけど、ここではEngineersという型が定義されている。

オブジェクトの中身に、FrontendかBackendという文字列しか持たないtypeというキーを持つオブジェクトで、さらに最後に配列を表す[ ]がついているので、Engineersという型は配列であり、配列の各要素は指定されたキーを持つオブジェクトになる。

Engineers型のengineersという配列が定義されている。

期待される値の初期状態を作って、そこに条件に一致した場合1を足していく処理をしたいので、reduceを使う。

初期値に以下を設定したいので

{ Frontend: 0, Backend: 0 }

reduceの第二引数に設定する。これが1周目の蓄積値になる。

const engineerType = engineers.reduce(
  (sum, { type }) => {
    if (type === 'Frontend') {
      sum.Frontend++
    } else {
      sum.Backend++
    }
    return sum
  },
  { Frontend: 0, Backend: 0 }
)

reduceのコールバック関数の第一引数は蓄積値、第二引数は現在の要素が入る。1周目の蓄積値(sum)が初期値になって、現在の要素({ type })は0番目の要素が入る。

{ type }は分割代入で、1周目は{ type: 'Frontend' }が入ってきて、キーがtypeの値だけを引っこ抜けるので、’Frontend’になる。

{ Frontend: 1, Backend: 0 }になる。

これを繰り返すと期待する値である

expect: {Frontend: 3, Backend: 4}

が取得できる。

問題15

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑮

キーと値を入れ替えたい時は、entriesを使う。

プロパティごとの配列を作ってくれる。

const array = Object.entries(obj);

[
	['a', 'A'],
	['b', 'B']
]

mapを使うことで、配列の要素を使用して新しい形の配列を作る。

文字列を[ ]で囲んでオブジェクトのキーにすると変数に展開することができる。

mapの引数にコールバック関数を渡して、配列から1つずつ要素が入ってきて、1番目の要素をオブジェクトのキーとし、0番目の要素をオブジェクトの値とすることで期待される値を作れる。

array.map((el) => { [el[1]]: el[0] });

問題16

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑯

Alpha型は、オブジェクトであり、オブジェクトのキーは文字列を[]で囲んでいるので、文字列の””を外した値になり、値は文字列になる

オブジェクトのキーに[”文字列”]とすると、変数に展開することができる。

reduceのコールバック関数の引数の第一引数は蓄積値、第二引数は現在の要素、第三引数はインデックス番号。

reduceの第二引数を指定すると、第二引数を初期値に設定できる。

なので、初めの計算では、prevは{ }、currは”a”、iは0、となる。

空のオブジェクト{ }が初期値になり、スプレッド構文で{ }を外すと何もない状態になる。

初めの戻り値は { a: “0” }

次の計算では、prevは{ a: “0” }、currは”b”、iは1、となる。

次の戻り値は { a: “0”, b: “1” }

問題17

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑰

typeを使うことで独自の型を定義できるんだけど、ここではFruitsobjという型とFruitsという型が定義されている。

Fruitsobjはオブジェクトの中身に、number型のpriceとnumber型のnumをキーに持つオブジェクトである。

Fruitsはオブジェクトの中身に、Fruitobj型の、string型のfruitを変数に展開したキーを持つオブジェクトである。

Fruits型のfruitsという配列が定義されている。

まず、オブジェクトのキーと値を要素に持つ配列に変換する。

オブジェクトから配列への変換は、Object.entries(配列)でできる。

[
	['みかん', {price:200, num:5}],
	['いちご', {price:400, num:1}],
	['ぶどう', {price:380, num:7}]
]

ここから、キーにみかんとぶどうを持つ要素だけに絞り込む。

絞り込む時はfilterを使う。

filterは配列の各要素から条件に合った要素だけに絞り込むことができる。

filterの引数にはコールバック関数を渡すんだけど、コールバック関数の引数には、filterをかける配列の各要素が1つずつ渡されてくる。なので、1つ目に渡されるのは['みかん', {price:200, num:5}]。入ってきた要素の0番目の要素がキーになるので、入ってきた要素[0]とすればよくて、それがみかんかぶどうなら新しい配列の要素として返される。以下の状態になる。

[
	['みかん', {price:200, num:5}],
	['ぶどう', {price:380, num:7}]
]

ゴールは以下のようになることなので、オブジェクトに戻したい。

const serectFruits = {
  みかん: { price: 200, num: 5 },
  ぶどう: { price: 380, num: 7 },
};

空のオブジェクトに{}、配列から必要な要素を引っこ抜いて追加していけばできそうなのでreduceを使う。

const selectFruits = arrr.reduce((prev, [k, v]) => {
  return { ...prev, [k]: v };
}, {});

reduceのコールバック関数の引数の第一引数は蓄積値、第二引数は現在の要素。

reduceの第二引数を指定すると、第二引数を初期値に設定できる。

なので、初めの計算では、prevは{ }、[k, v]は[”みかん”, { price: 200, num: 5 }]となる。

[ k, v ] = [”みかん”, { price: 200, num: 5 }]で分割代入されて、[ みかん, {price: 200, num: 5} ]となる。

戻り値は、空のオブジェクト{ }が初期値になり、スプレッド構文で{ }を外すと何もない状態になり、[”みかん”]は[ ]で文字列を囲んでいてオブジェクトのキーになるから変数に展開されて みかん となり、vは{ price: 200, num: 5 }が入ってくるので、

初めの戻り値は { みかん: { price: 200, num: 5 } }

次の計算では、prevは{ みかん: { price: 200, num: 5 } }、[ k, v ]は[”ぶどう”, { price: 380, num: 7 }]となる。

次の戻り値は { みかん: { price: 200, num: 5 }, ぶどう: { price: 380, num: 7 } }

問題18

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑱

Math.max()で最大値を取得できる。

const result = Math.max(...array);
// 300

引数には、配列ではなく、数値をカンマで並べるものが入るため、配列の[ ]を外すスプレッド構文を使用。

問題19

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑲

typeを使うことで独自の型を定義できる

ここではComplexJsonという型が定義されている。

オブジェクトの中身に、string型のfoodを[ ]で囲って変数に展開したキーとその値にはstring型の分類とstring型かobject型の主成分というキーを持つオブジェクトを持つ。

ComplexJson型のcomplexJsonというオブジェクトが定義されている。

まず、配列に変換したいのでObject.entries()を使う。

const entriesArray = Object.entries(complexJson);

すると、中身は以下のようになる

[
	["ハンバーグ", {分類: "洋食", 主成分: "タンパク質"}],
	["カレー", {分類: "洋食", 主成分: {ルー: "タンパク質", ライス: "炭水化物"}}],
	["親子丼", {分類: "和食", 主成分: {: "タンパク質", ご飯: "炭水化物"}}]
]

ここからハンバーグを0番目の要素に持つ要素だけに絞る

const filteredentriesArray = entriesArray.filter((el) => el[0] === "ハンバーグ");
// [
//	["ハンバーグ", {分類: "洋食", 主成分: "タンパク質"}],
// ]

そして、mapで返したい形を作る

filteredentriesArray.map((el) => [{[el[0]]: el[1]}]);

0番目の要素でハンバーグという値を取得し、[ ]で囲むことで変数展開してキーにする。

1番目の要素で分類と主成分のオブジェクトを取得し、値として設定する。

これで期待した値になる。

※おそらく期待される値はこれが正しいのではないかと予想している。キーと値を持っているので{ }で表現する必要があると思う。

問題20

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題⑳

srcDataの要素を使って新しい配列を作ればいいと思ったのでmap()が使えそう。

srcData.map((element) => {
	return { id: element[0], shop: element[1] }
});

mapは引数にコールバック関数を渡すことができて、コールバック関数の引数にはmapを使っている配列の各要素が1つずつ渡される。なので、1つ目は{ 0: 'id1', 1: '店A' }が渡される。戻り値としてreturnしたい形のオブジェクトを作って、要素のキーが0の値をidの値として設定し、要素のキーが1の値をshopの値として設定すれば期待した配列になる。

問題21

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題㉑

元々の配列からいろいろ計算して、違う姿の配列を作るにはreduceを使うとよさそう。

reduceのコールバック関数の引数の第一引数は蓄積値、第二引数は現在の要素。

reduceの第二引数を指定すると、第二引数を初期値に設定できる。

なので、初めの計算では、totalは[{ totalItems: 0, cartTotal: 0 }]、{ amount, price }は{ item: ‘いちご’, price: 150, amount: 3 }となり、分割代入で{ amount, price }は{ 3, 150 }となる。

totalは1つだけ要素が入ってる配列なので、total[0]とすることでその要素にアクセスできる。

アクセスした要素のtotalItemsプロパティの値にamoutの3を足し、cartTotalプロパティの値にpriceの150を足し、totalは[{ totalItems: 3, cartTotal: 150 }]となり、戻り値としてreturnされる。

次の計算では、totalは[{ totalItems: 3, cartTotal: 150 }]、{ amount, price }は{ item: ‘みかん’, price: 250, amount: 3 }となり、同じように計算されてreturnされる。

最後の要素まで繰り返すと期待される値になる。

const total = cart.reduce(
  (total, { amount, price }) => {
    total[0]['totalItems'] += amount
    total[0]['cartTotal'] += price
    return total
  },
  [
    {
      totalItems: 0,
      cartTotal: 0
    }
  ]
)

問題22

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題㉒

変数pagesには、引数に数値型のitemPerを渡せる関数の戻り値が代入される。

関数の処理内容は、配列の要素数(items.length)をitemPerで割った値を切り上げた値が入る。

Math.ceil()では引数に入れた値を切り上げた値を取得できる。

newItems1には、配列itemsを10個の要素ごとに分割した値を入れる。

Array.from()は、map()のように配列の要素を一つずつコールバック関数に渡して、コールバック関数の戻り値から新しい配列を作成できたり、lengthで要素数を渡してその要素数を持った配列の1つ1つに戻り値を入れたりすることができて、今回は後者の使い方になる。

itemPerPage1は10なので、10個の要素を持つ配列が作成される。第二引数のコールバック関数の引数の第一引数にはundefinedが渡されて、第二引数のindexにはインデックス番号が渡される。

なので、1周目は、

const start = 0 * 10;
const tempItems = item.slice(0, 0 + 10);

となり、[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]がreturnされる

2周目は、

const start = 1 * 10;
const tempItems = item.slice(10, 10 + 10);

となり、[ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ]がreturnされる

最後の要素まで繰り返すことで、10個の要素ごとに分割した配列を作成できる

newsItems2でも同じ考えで処理をすることで、14個の要素ごとに分割した配列を作成できる。

const itemPerPage1 = 10;
const itemPerPage2 = 14;

const pages = (itemPer: number) => {
  return Math.ceil(item.length / itemPer);
};

const newItems1 = Array.from({ length: pages(itemPerPage1) }, (_, index) => {
  const start = index * itemPerPage1;
  const tempItems = item.slice(start, start + itemPerPage1);

  return tempItems;
});

const newItems2 = Array.from({ length: pages(itemPerPage2) }, (_, index) => {
  const start = index * itemPerPage2;
  const tempItems = item.slice(start, start + itemPerPage2);

  return tempItems;
});

console.log(newItems1);//[ [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ], [ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ], [ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 ] ] 
console.log(newItems2);//[ [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ], [ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 ], [ 29, 30 ] ]

問題23

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題㉓
期待される値が載っていないので、自分で決めてそれに向かって処理を作ってみた。

membersRelationというオブジェクトの中に、doryoとkohaiというプロパティを持たせて関係を表現することにした。

const membersRelation = {
	doryo: [],
	kohai: []
}

typeを使うことで独自の型を定義できるんだけど、ここではMembersという型が定義されている。

オブジェクトの中身に、string型のnameとnumber型のageとmaleかfemaleという文字列しか持たないgenderというキーを持つオブジェクトで、さらに最後に配列を表す[ ]がついているので、Membersという型は配列であり、配列の各要素は指定されたキーを持つオブジェクトになる。

Members型のmembersという配列が定義されている。

各要素を使って、繰り返し処理をしたいのでmapを使う。

members.map((el) => {
	if (el.name === "鈴木") {
		membersRelation.doryo.push(el);
	} else if (el.name === "松井" || el.name === "今田") {
		membersRelation.kohai.push(el);
	}
});

mapは引数にコールバック関数を渡すことができて、コールバック関数の引数にはmapを使っている配列の各要素が1つずつ渡される。なので、1つ目は{ name: "松井", age: 21, gender: "male" }が渡される。この配列のnameプロパティが”鈴木”ならmembersRelationのdoryoプロパティに追加し、”松井”か”今田”ならmembersRelationのkohaiプロパティに追加する。

これを最後の要素まで繰り返すことで、membersRelationで関係を表現することができる。

const membersRelation = {
		doryo: [
			{ name: "鈴木", age: 27, gender: "male" },
		],
		kohai: [
			{ name: "松井", age: 21, gender: "male" },
		  { name: "今田", age: 23, gender: "female" },
		]
};

※もしかして、こういうときはmapを使わないほうがいいのだろうか。mapは新しい配列を返すものだから、新しい配列を作る必要がない繰り返し処理にはfor文のほうがいいのだろうか。。

問題24

https://zenn.dev/tsuboi/articles/c4f17185a6181f013108#問題㉔

問題を参考に、奇数の配列と偶数の配列を作る

const kisuu = [1, 3, 5];
const guusuu = [2, 4, 6];

この2つの配列を1つの配列にまとめてしまって、小さい順に並べ替えれば期待されてる値になると思った。その処理をする関数を作る。

[ ]の中でkisuuとguusuuをスプレッド構文を使って[ ]を外し、カンマで繋げば1つの配列になる。

1つになった配列をsortを使って小さい順から並び替え。

sortは、以下のルールがある

コールバック関数が0未満(例えば-1)を返した場合:aはbの前に来る(順番は変わらない)
コールバック関数が0より大きい値(例えば1)を返した場合:bはaの前に来る(順番が変わる)
コールバック関数が0を返した場合:何もしない
https://dezanari.com/js-sortk/

このルールを使うと、a - bをすることで小さい順に並び替えることができる

const getNatureNumber = (kisuu, guusuu) => {
	const kisuutoguusuu = [...kisuu, ...guusuu];
	return kisuutoguusuu.sort((a, b) => a - b);
}
// [1, 2, 3, 4, 5, 6]

Discussion

TsuboiTsuboi

丁寧に解説して頂きありがとうございます!

引用元では、index === 0 && keyとなっていて、たぶん意味があると思うけどわからなかった。もしわかる方いたら教えていただきたいです。

こちら&& keyは必要ないですね。失礼しました🙇‍♂️
おそらく当時は以下のような警告が出るのが嫌でindex === 0 && keyとしたのだと思います。

使用しないのであれば、アンダースコアでいいですね。

const arr = array.filter((_, index) => index === 0)
taichitaichi

Tsuboiさん!コメントいただき嬉しいです。
そういうことだったのですね。とてもスッキリいたしました。
ありがとうございます🙇‍♂️