20210304のJavaScriptに関する記事は22件です。

【Mac】【Windows】【VSCode】シンプルにTypeScriptの導入する方法

この記事は何?

TypeScriptを導入する方法を紹介する記事です。
とりあえず、さくっと使ってみたい人向けになります。

TypeScriptとは何?という人は、以下の記事を御覧ください。
? JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事

導入パターン

TypeScriptには、導入方法が2つあります。
- npm(Node.jsパッケージマネージャー)経由
- TypeScriptのVisualStudioプラグインをインストールする

今回は、npm経由について紹介します。
※ ただし、今回は、説明のためにVSCodeも利用します。他のエディタでも問題なく動作します。

また、スクリーンショットはMacですが、Windowsも問題なく利用できます。

各種インストール

① まず、VSCodeをこのリンクからインストールします。自分が持っているPCのOSに合わせて、選択しましょう!
image.png

また、nodeをインストールしていない場合は、nodeもインストールしておきましょう!このリンクからインストール出来ます。LTS版をインストールしてください。
image.png

② VSCodeとnode.jsのインストールが終わったら、VSCodeを開きます
image.png

③ VSCodeを開いたらTerminal > NewTerminalを開きます。
image.png

プロジェクト作成の準備

④ ターミナルが開けたら、TypeScript用のプロジェクトを作成します。まずは、ディレクトリ(フォルダ)を作成しましょう。

mkdir new-typescript

image.png

⑤ ディレクトリを移動します

cd new-typescript

⑥ 次に、プロジェクト(パッケージ)を管理するpackage.jsonというものを以下のコマンドで作成します。

npm init

image.png

⑦ 色々と効かれますが、全てエンターを押せば問題ありません。最後に、Is this OK? (yes)でエンターを押せばpackage.jsonが作成されます。
image.png

package.jsonが出来たら、TypeScriptを以下のコマンドでインストールしていきます。これにより、このプロジェクト内でのみTypeScriptが使えるようになります。package.jsontypescriptの記述があれば問題ありません。

npm install typescript --save-dev

image.png

TypeScriptを使ってみる

⑨ お疲れさまです!これで準備が整いました。次は、簡単にTypeScriptを触ってみましょう。新しく.tsがつくファイルを作成します。
image.png

⑩ 以下のように、ファイルに記述します。
string型の引数を持つsayHello関数
sayHello関数の呼び出し

const sayHello = (name: string) => {
    console.log(`${name}さん、こんにちは!`):
}

sayHello('煉獄さん');

'煉獄さん'はstring型なので、仮に数値を入れるとどうなるでしょうか。以下のように、警告を出してくれます。
image.png

⑫ 最後に実行してみましょう! この.tsファイルはそのままでは、ブラウザでは読み込めません。そのため、.jsファイルに変換します。実行するには、以下のコマンドを実行します。

npx tsc hello.ts

⑬ さきほどのファイルは、TypeScriptの規則に違反しているので、次のようにエラーが発生します。
image.png

⑭ そのため、コードを修正し、再実行します。そうすると、.jsファイルが自動生成されます。
image.png

⑮ 最後に、.jsファイルを実行します。問題なく動作すれば、コンソールに煉獄さんさん、こんにちは!と表示されます。

$ node hello.js
煉獄さんさん、こんにちは!

以上です!
実際に、TypeScriptを触ってみて、一つ上のJavaScript体験を体験しましょう!!

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Mac】【Windows】【VSCode】シンプルにTypeScriptの使う方法

この記事は何?

TypeScriptを導入する方法を紹介する記事です。
とりあえず、さくっと使ってみたい人向けになります。

TypeScriptとは何?という人は、以下の記事を御覧ください。
? JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事

目次
・ 導入パターン
・ インストールとTYpeScriptを使う準備
 - 各種インストール
 - プロジェクト作成の準備 ← VScodeもnode.jsも入っている方はこちらから
 - TypeScriptを使ってみる
・ 参考文献

導入パターン

TypeScriptには、導入方法が2つあります。
- npm(Node.jsパッケージマネージャー)経由
- TypeScriptのVisualStudioプラグインをインストールする

今回は、npm経由について紹介します。
※ ただし、今回は、説明のためにVSCodeも利用します。他のエディタでも問題なく動作します。

また、スクリーンショットはMacですが、Windowsも問題なく利用できます。

インストールとTYpeScriptを使う準備

各種インストール

① まず、VSCodeをこのリンクからインストールします。自分が持っているPCのOSに合わせて、選択しましょう!
image.png

また、nodeをインストールしていない場合は、nodeもインストールしておきましょう!このリンクからインストール出来ます。LTS版をインストールしてください。
image.png

② VSCodeとnode.jsのインストールが終わったら、VSCodeを開きます
image.png

③ VSCodeを開いたらTerminal > NewTerminalを開きます。
image.png

プロジェクト作成の準備

④ ターミナルが開けたら、TypeScript用のプロジェクトを作成します。まずは、ディレクトリ(フォルダ)を作成しましょう。

mkdir new-typescript

image.png

⑤ ディレクトリを移動します

cd new-typescript

⑥ 次に、プロジェクト(パッケージ)を管理するpackage.jsonというものを以下のコマンドで作成します。

npm init

image.png

⑦ 色々と効かれますが、全てエンターを押せば問題ありません。最後に、Is this OK? (yes)でエンターを押せばpackage.jsonが作成されます。
image.png

package.jsonが出来たら、TypeScriptを以下のコマンドでインストールしていきます。これにより、このプロジェクト内でのみTypeScriptが使えるようになります。package.jsontypescriptの記述があれば問題ありません。

npm install typescript --save-dev

image.png

TypeScriptを使ってみる

⑨ お疲れさまです!これで準備が整いました。次は、簡単にTypeScriptを触ってみましょう。新しく.tsがつくファイルを作成します。
image.png

⑩ 以下のように、ファイルに記述します。
string型の引数を持つsayHello関数
sayHello関数の呼び出し

const sayHello = (name: string) => {
    console.log(`${name}さん、こんにちは!`):
}

sayHello('煉獄さん');

'煉獄さん'はstring型なので、仮に数値を入れるとどうなるでしょうか。以下のように、警告を出してくれます。
image.png

⑫ 最後に実行してみましょう! この.tsファイルはそのままでは、ブラウザでは読み込めません。そのため、.jsファイルに変換します。実行するには、以下のコマンドを実行します。

npx tsc hello.ts

⑬ さきほどのファイルは、TypeScriptの規則に違反しているので、次のようにエラーが発生します。
image.png

⑭ そのため、コードを修正し、再実行します。そうすると、.jsファイルが自動生成されます。
image.png

⑮ 最後に、.jsファイルを実行します。問題なく動作すれば、コンソールに煉獄さんさん、こんにちは!と表示されます。

$ node hello.js
煉獄さんさん、こんにちは!

以上です!
実際に、TypeScriptを触ってみて、一つ上のJavaScript体験を体験しましょう!!

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Mac】【Windows】【VSCode】シンプルにTypeScriptを導入して使う方法

この記事は何?

TypeScriptを導入する方法を紹介する記事です。
とりあえず、さくっと使ってみたい人向けになります。

この記事がわかりやすかったらLGTM✨頂けると嬉しいです!

また、TypeScriptとは何?という人は、以下の記事を御覧ください??
? JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事

目次
・ 導入パターン
・ インストールとTYpeScriptを使う準備
 - 各種インストール
 - プロジェクト作成の準備VScodeもnode.jsもPCに入っている方はこちらから
 - TypeScriptを使ってみる
・ 参考文献

導入パターン

TypeScriptには、導入方法が2つあります。
- npm(Node.jsパッケージマネージャー)経由
- TypeScriptのVisualStudioプラグインをインストールする

今回は、npm経由について紹介します。
※ ただし、今回は、説明のためにVSCodeも利用します。他のエディタでも問題なく動作します。

また、スクリーンショットはMacですが、Windowsも問題なく利用できます。

インストールとTypeScriptを使う準備

各種インストール

① まず、VSCodeをこのリンクからインストールします。自分が持っているPCのOSに合わせて、選択しましょう!
image.png

また、nodeをインストールしていない場合は、nodeもインストールしておきましょう!このリンクからインストール出来ます。LTS版をインストールしてください。
image.png

② VSCodeとnode.jsのインストールが終わったら、VSCodeを開きます
image.png

③ VSCodeを開いたらTerminal > NewTerminalを開きます。
image.png

プロジェクト作成の準備

④ ターミナルが開けたら、TypeScript用のプロジェクトを作成します。まずは、ディレクトリ(フォルダ)を作成しましょう。

mkdir new-typescript

image.png

⑤ ディレクトリを移動します

cd new-typescript

⑥ 次に、プロジェクト(パッケージ)を管理するpackage.jsonというものを以下のコマンドで作成します。

npm init

image.png

⑦ 色々と効かれますが、全てエンターを押せば問題ありません。最後に、Is this OK? (yes)でエンターを押せばpackage.jsonが作成されます。
image.png

package.jsonが出来たら、TypeScriptを以下のコマンドでインストールしていきます。これにより、このプロジェクト内でのみTypeScriptが使えるようになります。package.jsontypescriptの記述があれば問題ありません。

npm install typescript --save-dev

image.png

TypeScriptを使ってみる

⑨ お疲れさまです!これで準備が整いました。次は、簡単にTypeScriptを触ってみましょう。新しく.tsがつくファイルを作成します。
image.png

⑩ 以下のように、ファイルに記述します。
string型の引数を持つsayHello関数
sayHello関数の呼び出し

const sayHello = (name: string) => {
    console.log(`${name}さん、こんにちは!`):
}

sayHello('煉獄さん');

'煉獄さん'はstring型なので、仮に数値を入れるとどうなるでしょうか。以下のように、警告を出してくれます。
image.png

⑫ 最後に実行してみましょう! この.tsファイルはそのままでは、ブラウザでは読み込めません。そのため、.jsファイルに変換します。実行するには、以下のコマンドを実行します。

npx tsc hello.ts

⑬ さきほどのファイルは、TypeScriptの規則に違反しているので、次のようにエラーが発生します。
image.png

⑭ そのため、コードを修正し、再実行します。そうすると、.jsファイルが自動生成されます。
image.png

⑮ 最後に、.jsファイルを実行します。問題なく動作すれば、コンソールに煉獄さんさん、こんにちは!と表示されます。

$ node hello.js
煉獄さんさん、こんにちは!

以上です!
実際に、TypeScriptを触ってみて、一つ上のJavaScript体験を体験しましょう!!

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【画像で説明】シンプルにTypeScriptを導入して使う方法

この記事は何?

TypeScriptを導入する方法を紹介する記事です。
とりあえず、さくっと使ってみたい人向けになります。

この記事がわかりやすかったらLGTM✨頂けると嬉しいです!

また、TypeScriptとは何?という人は、以下の記事を御覧ください??
? JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事

目次
・ 導入パターン
・ インストールとTYpeScriptを使う準備
 - 各種インストール
 - プロジェクト作成の準備VScodeもnode.jsもPCに入っている方はこちらから
 - TypeScriptを使ってみる
・ 参考文献

導入パターン

TypeScriptには、導入方法が2つあります。
- npm(Node.jsパッケージマネージャー)経由
- TypeScriptのVisualStudioプラグインをインストールする

今回は、npm経由について紹介します。
※ ただし、今回は、説明のためにVSCodeも利用します。他のエディタでも問題なく動作します。

また、スクリーンショットはMacですが、Windowsも同じ手順で問題なく利用できます?

インストールとTypeScriptを使う準備

各種インストール

① まず、VSCodeをこのリンクからインストールします。自分が持っているPCのOSに合わせて、選択しましょう!
image.png

また、nodeをインストールしていない場合は、nodeもインストールしておきましょう!このリンクからインストール出来ます。LTS版をインストールしてください。
image.png

② VSCodeとnode.jsのインストールが終わったら、VSCodeを開きます
image.png

③ VSCodeを開いたらTerminal > NewTerminalを開きます。
image.png

プロジェクト作成の準備

④ ターミナルが開けたら、TypeScript用のプロジェクトを作成します。まずは、ディレクトリ(フォルダ)を作成しましょう。

mkdir new-typescript

image.png

⑤ ディレクトリを移動します

cd new-typescript

⑥ 次に、プロジェクト(パッケージ)を管理するpackage.jsonというものを以下のコマンドで作成します。

npm init

image.png

⑦ 色々と効かれますが、全てエンターを押せば問題ありません。最後に、Is this OK? (yes)でエンターを押せばpackage.jsonが作成されます。
image.png

package.jsonが出来たら、TypeScriptを以下のコマンドでインストールしていきます。これにより、このプロジェクト内でのみTypeScriptが使えるようになります。package.jsontypescriptの記述があれば問題ありません。

npm install typescript --save-dev

image.png

TypeScriptを使ってみる

⑨ お疲れさまです!これで準備が整いました。次は、簡単にTypeScriptを触ってみましょう。新しく.tsがつくファイルを作成します。
image.png

⑩ 以下のように、ファイルに記述します。
string型の引数を持つsayHello関数
sayHello関数の呼び出し

const sayHello = (name: string) => {
    console.log(`${name}さん、こんにちは!`):
}

sayHello('煉獄さん');

'煉獄さん'はstring型なので、sayHelloにnumber型を入れるとどうなるでしょうか。
以下のように、警告を出してくれます。

const sayHello = (name: string) => {
    console.log(`${name}さん、こんにちは!`):
}

sayHello('煉獄さん');
sayHello(1); // タイプ 'number'の引数をタイプ 'string'のパラメーターに割り当てることはできません

image.png

⑫ 最後に実行してみましょう! この.tsファイルはそのままでは、ブラウザでは読み込めません。そのため、.jsファイルに変換します。実行するには、以下のコマンドを実行します。

npx tsc hello.ts

⑬ さきほどのファイルは、TypeScriptの規則に違反しているので、次のようにエラーが発生します。
image.png

⑭ そのため、コードを修正し、再実行します。そうすると、.jsファイルが自動生成されます。
image.png

⑮ 最後に、.jsファイルを実行します。問題なく動作すれば、コンソールに煉獄さんさん、こんにちは!と表示されます。

$ node hello.js
煉獄さんさん、こんにちは!

以上です!
実際に、TypeScriptを触ってみて、一つ上のJavaScript体験を味わいましょう!!

@ ハンズオンも書いたので、さっそく使ってみたい人は是非!
 ?【TypeScriptハンズオン①】男もすなるTypeScriptといふものを、女もしてみむとてするなり

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発】Vueとajaxを使用したWebサービスを作ってみた

はじめまして きゃっぷ@capgame_です。
ガチャのようなサービスを作るためにVueや非同期通信について学んだので、そのことについて書いていきます。

作ったもの

サイトのスクリーンショット

こんな感じで、ランダムで(投稿された)言葉を組み合わせて、文章を作れるサービスです。
現時点はで100words以上投稿されています。

技術

メイン部分 : JavaScript

言葉を組み合わせて文章を作るってのは

main.js
    console.log(["","",""][parseInt(Math.random() * 3)] + ["佐藤が","田中が","鈴木が"][parseInt(Math.random() * 3)] + ["叫んだ","泣いた","笑った"][parseInt(Math.random() * 3)]) 

みたいなコードで書くことができ、入門書のサンプルによく使われるほどの簡単なプログラムです。

これに、jQueryの$.get()で言葉を取得しています。

投稿部分 : jQuery

こちらもjQueryの $.get() を使って言葉を投稿しています。

そして、データベースは使っていません。
phpのfile_put_contents()及びfile_get_contents()を使ってファイルでセーブしています。
便利ですがちゃんと . とか / のエスケープは行いましょう。

CSS : SCSS

SCSSを使って書いてみました。
ヘッダー部分とか入れ子になっていると分かりやすいのでかなり書きやすかったです。
SCSS布教活動したいです。

最後に

今までかたくなにライブラリを使わなかったのを後悔するくらいVueで要素いじるのが楽でした。
今回のサイトでは、v-modelとv-bindとv-onくらいしか使ってないので、もっといろいろ学んでみたいと思いました。

あとこのサイトに単語たくさん追加してください?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【javascript】appendChildを多用するコードをスッキリと

目的

自分用

HTML
<div class="e1">
    <div class="e1-1">
        <div class="e1-1-1"></div>
        <div class="e1-1-2">
            <div class="e1-1-2-1">
        </div>
        <div class="e1-1-3"></div>
    </div>
</div>

javascriptで上記のような要素の生成と追加を繰り返すコードをスッキリさせたかった。
下記のように書くと上記HTMLを出力するようにしたい。

const c = 'className';
const elmement =
    childAppender(el('div',{[c]:'e1'}),
        siblingAppender(el('div',{[c]:'e1-1'}),
            el('div',{[c]:'e1-1-1'}),
            childAppender(el('div',{[c]:'e1-1-2'}),
                el('div',{[c]:'e1-1-2-1'})
            ),
            el('div',{[c]:'e1-1-3'})
        )
    );
console.log(elmement);  //上記HTMLのような構造を出力

第一引数は親要素となる為、慣れないと少しややこしい。

関数

以下の関数を用いる

//要素の生成
const el = (tag, attr) => {
    if (!tag) return null;
    const elm = document.createElement(tag);
    if (!attr) return elm;
    const map = Object.entries(attr);
    for (const [k,v] of map) elm[k] = v;
    return elm;
};
//nodeTypeの確認
const isElementNode = n => n ? n.nodeType===1 : false;
//子孫要素の追加
const childAppender = (...elms) => {
    const idx = elms.findIndex(x => !isElementNode(x));
    switch (idx) {
        case -1:    break;
        case 0:     return null;
        case 1:     return elms[0];
        default:    elms.slice(0, idx); break;
    }
    for (let i=0,len=elms.length-1; i<len; i++) {
        elms[i].appendChild(elms[i + 1]);
    }
    return elms[0];
};
//兄弟要素の追加
const siblingAppender = (parent, ...elms) => {
    if (!isElementNode(parent)) return null;

    const available = elms.filter(isElementNode);
    for (const child of available) parent.appendChild(child);
    return parent;

};

関数の説明

  • el( tagName[, {attribute: value,...}] )
    要素を生成し返す。
    引数1:tagName => HTMLのタグ名
    引数2:属性名をKEY、値をVALUEで組んだオブジェクト形式
        elm.className = 'classname'のようなイコールで指定出来るもののみ
        elm.classList.add('classname')のような指定方法のものは不可

  • childAppender( element1, element2, ... )
    第一引数を親要素として残りの引数を順次、直列的に子孫に追加し、親要素を返す。
    nodeTypeが1でない要素が存在すると以降の要素は追加されない。

  • siblingAppender( parentNode, element1, element2, ... )
    第一引数を親要素として残りの引数を順次、兄弟要素として親要素の直下に追加し、親要素を返す。
    nodeTypeが1でない要素は追加されないが、childAppenderと違い最後の引数まで追加される。

感想

少し重い気がする。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

配列に入ったvalueが重複しているオブジェクトを取り除きたい

目的

user_idがかぶってるやつを取り除きたい。
(あとからDBに追加されたレコードを優先したりさせなかったりしたい。)

【前提となる配列】

const items = [
  { id: 1, user_id: 1, num: 1 },
  { id: 2, user_id: 2, num: 2 },
  { id: 3, user_id: 1, num: 11 },
  { id: 4, user_id: 2, num: 12 },
  { id: 5, user_id: 3, num: 13 },
];

①idが大きいものを優先(上書き)しながら重複を取り除く

Mapを使う方法

const updateItems = [...new Map(items.map((value) => [value.user_id, value])).values()];

console.log(updateItems);
// [ { id: 3, user_id: 1, num: 11 },
//   { id: 4, user_id: 2, num: 12 },
//   { id: 5, user_id: 3, num: 13 } ]

sortとfilterを組み合わせる方法

const updateItems = items
  .sort((a, b) => {
    return b.id - a.id;
  })
  .filter((item, index, array) => {
    return array.findIndex((item2) => item.user_id === item2.user_id) === index;
  });

console.log(updateItems);
// [ { id: 5, user_id: 3, num: 13 },
//   { id: 4, user_id: 2, num: 12 },
//   { id: 3, user_id: 1, num: 11 } ]

②idが小さいものを優先しながら重複を取り除く

const updateItems = items
    .sort((a, b) => b.user_id - a.user_id)
    .filter((value, index, array) => {
      return array.findIndex((value2) => value.user_id === value2.user_id) === index;
    });

console.log(updateItems);
// [ { id: 5, user_id: 3, num: 13 },
//   { id: 2, user_id: 2, num: 2 },
//   { id: 1, user_id: 1, num: 1 } ]

他にもこんな方法があるよ!があったら是非教えていただきたいです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptでPythonのzip関数を実装してみました

Python3にはzip()という便利な組み込み関数があります。
以下のように、複数のイテラブル1を同時にイテレーションできる関数です。

chars = ["a", "b", "c"]
nums = [1, 2, 3, 4, 5]

for elems in zip(chars, nums):
    print(elems)

# 出力
# ('a', 1)
# ('b', 2)
# ('c', 3)

引数にイテラブルを複数指定すると、各イテラブルの要素をまとめたタプルを返すイテレータを作ってくれます。
イテラブル間で要素数が異なる場合は、もっとも短いイテラブルの要素が尽きた時点で止まります。

この関数をJavaScriptで再現してみます。

環境と背景知識

先行事例

「JavaScript Python zip」で検索すると似たような試みは何件かヒットします。
Qiitaでも先駆者がいらっしゃいます。

個人的にいつも参考にしている民主主義に乾杯さんから実装例を引用します。

function* zip(...args) {

    const length = args[0].length;

    // 引数チェック
    for (let arr of args) {
        if (arr.length !== length){
            throw "Lengths of arrays are not eqaul.";
        }
    }

    // 
    for (let index = 0; index < length; index++) {
        let elms = [];
        for (arr of args) {
            elms.push(arr[index]);
        }
        yield elms;
    }
}

空の配列elmsを作る→イテラブルのそれぞれi番目の要素を取得して順次elmsにpush→elmsをyield、を繰り返す流れです。
配列間の要素数が同じである必要はあるものの、Pythonのzip関数と同じようなことができそうです。
イテレータを返すところも再現度が高くて良いですね。

yucatioさんの実装もご紹介します。

const zip = (...arrays) => {
  const length = Math.min(...(arrays.map(arr => arr.length)))
  return new Array(length).fill().map((_, i) => arrays.map(arr => arr[i]))
}

こちらはジェネレータ関数ではなく普通の関数(アロー関数)としての実装で、返すのはイテレータではなく配列になります。
この実装が優れている点の一つは、配列間の要素数が違っていても、最も短い配列に合わせて処理してくれるところです(2行目)。
3行目はmap()が入り乱れていたり、謎のfill()が挟まっていたりと少々読みづらいですが、元サイトで詳しく解説されているのでご覧になってみてください。

先行事例についてさらに考察しましたが、ちょっと長くなったので折りたたみ。

改良してみた

上記二つのコードを組み合わせてみます。

function* zip(...args) { 
    const length = Math.min(...args.map(arg => arg.length));
    for (let index = 0; index < length; index++) {
        let elms = [];
        for (const arr of args) {
            elms.push(arr[index]);
        }
        yield elms;
    }
}

引数のイテラブルの要素数が異なっても対応でき、さらにPythonのzip()関数と同じくイテレータを返すようになっています。
これでなかなかの再現度になったのではないでしょうか?

使ってみましょう。

const chars = ["a", "b", "c"];
const nums = [1, 2, 3, 4, 5];

for (const elems of zip(chars, nums)) {
    console.log(elems);
}

// 出力
// Array [ "a", 1 ]
// Array [ "b", 2 ]
// Array [ "c", 3 ]

Pythonと同じ結果が得られました!

また、Pythonのzip()はリスト2以外でも、文字列などイテラブルであれば何でも引数にとれます。
JavaScript版zip()でも文字列を引数にして試してみます。

const str = "qwe";
const nums = [1, 2, 3, 4, 5];

for (const elems of zip(str, nums)) {
    console.log(elems);
}

// 出力
// Array [ "q", 1 ]
// Array [ "w", 2 ]
// Array [ "e", 3 ]

いけますね。
その他、NodeListやargumentsオブジェクトなどのイテラブルにも使うことができます。
このzip関数ならどんなイテラブルが来ても怖くありません。

本当に?

添字アクセスができないと使えない

たしかに、JavaScriptの代表的なイテラブルである配列や文字列は問題なくイテレートできます。
それでは、先ほどのzip()でSetオブジェクトをイテレートしてみます。

const str = "qwe";
const numSet = new Set([1, 2, 3, 4, 5]);

for (const elems of zip(str, numSet)){
    console.log(elems);
}

// 出力
// 

…ダメですね。期待通りにイテレートしません。
Setオブジェクトは間違いなくイテラブルのはずなんですが。

原因はzip()の定義の6行目、elms.push(arr[index]);の部分で行われている添字アクセスです。
Setオブジェクトでは、numSet[1]のような添字を使った形で要素にアクセスすることができない3ので、意図した通りに処理されないのです。

Mapオブジェクトやジェネレータオブジェクトも同様に、イテラブルかつ添字アクセスができないオブジェクトです。

本編です

折りたたみを読んでくださった方、ありがとうございました。ここから本編です。
折りたたみを飛ばした方、大丈夫です。本編はここからです。

つくるもの

Pythonのzip()に似せるため、以下の条件を満たすものを目指します。

  • 引数のイテラブルをまとめてイテレートする機能を提供する
  • 実行したらイテレータを返す
  • イテレートはもっとも短いイテラブルの要素が尽きたら終了する
  • イテラブルならなんでも、いくつでも対応可能である。

方針

Pythonの公式ドキュメントのzip()の項目を見てみると、ありがたいことにzip()と等価なコードが紹介されています。
引用します。

def zip(*iterables):
    # zip('ABCD', 'xy') --> Ax By
    sentinel = object()
    iterators = [iter(it) for it in iterables]
    while iterators:
        result = []
        for it in iterators:
            elem = next(it, sentinel)
            if elem is sentinel:
                return
            result.append(elem)
        yield tuple(result)

このコードのzip()は組み込みのほうのzip()と等価なので、当然「つくるもの」の項で挙げた条件を満たします。
これをJavaScriptで書き直せばよさそうです。

解読します

上のコードの処理を解読します。

全体としては中にyieldがある関数宣言の形ですので、ジェネレータ関数です。

sentinel = object()

どういうわけかobject()で空のオブジェクトsentinelを生成しています。
この段階では何のためかわかりません。とりあえず保留します。

iterators = [iter(it) for it in iterables]

引数のイテラブルからiter()でイテレータを取得して、iteratorsという名前のリストに格納しています。
リスト内包表記でiterablesの全ての要素について一括で処理していますね。
[イテラブル1, イテラブル2, イテラブル3,…]から[イテレータ1, イテレータ2, イテレータ3,…]を作っている感じです。

続いてwhile iterators:でループが始まっています。
もしiteratorsが空ならここはスルーされてなにもyieldされないので、空のジェネレータオブジェクトになります。

#ループ内
result = []
for it in iterators:
    elem = next(it, sentinel)
    if elem is sentinel:
        return
    result.append(elem)
yield tuple(result)

ループの中では、ループ毎にresultという空の配列が作られています。
そのあとfor文でiteratorsを走査しています。
具体的には、「イテレータからnext()で値を取り出す→resultに追加」を全てのイテレータについて行っています。
resultへの追加が完了したら、タプルに変換してyieldしています。

ここで、始めに作られた謎のsentinelがnext()の第二引数に使われていますね。
next()のドキュメントを確認します。

next(iterator[, default])
iterator の __next__() メソッドを呼び出すことにより、次の要素を取得します。イテレータが尽きている場合、 default が与えられていればそれが返され、そうでなければ StopIteration が送出されます。

next(it, sentinel)の直後にif文でelem is sentinelが評価されていることを考え合わせると、イテレータが尽きているかどうかをnext()の結果がsentinelかどうかで判断しているようです。
つまり、「イテレータが尽きる→elem is sentinelがTrueになる→returnしてジェネレータを終了」ということ。
「もっとも短いイテラブルの要素が尽きたら終了」という仕様がこういう形で実現できるんですね。

いざ、JavaScriptで実装

大事な点が一つ。
機能的には、JavaScriptではイテレータ.next()が、Pythonのnext(イテレータ)に相当します。
しかし、両者の戻り値は異なります。
Pythonのnext(イテレータ)は値を直接返します(例えば、ジェネレータならyieldされた値をそのまま返します)。イテレータが尽きていた場合、StopIteration を送出するか、第二引数の値を返します。
一方、JavaScriptのイテレータ.next()が返すのは、valueとdoneというプロパティを持つオブジェクトです。イテレータが実際に返した値はこのオブジェクトのvalueプロパティを参照して取得します。また、イテレータが尽きていた場合、doneプロパティがtrueになります(そうでない場合はfalseです)。

Pythonのコードではイテレータが尽きたかチェックするのにnext()の第二引数を利用していましたが、JavaScriptでは戻り値のdoneプロパティを参照すればよいので空オブジェクトsentinelを用意する必要はありませんね。

ではコードを書きます。
ジェネレータ関数なのでfunction*宣言の形にします。

function* zip(...iterables) {

}

次にiteratorsを作りますが、JavaScriptにはリスト内包表記はないのでmap()で代用。
また、イテラブルからイテレータを取得するiter(it)に相当するのは、JavaScriptではit[Symbol.iterator]()4 5です。

const iterators = iterables.map(it => it[Symbol.iterator]());

あとはwhileの部分です。
JavaScriptとPythonのnext()の違いに注意。
また、Pythonではresultをタプルにしてからyieldしていますが、JavaScriptにタプルはないので配列のままyieldします6

while (iterators) {
    const result = [];
    for (const it of iterators) {
        const elemObj = it.next();
        if (elemObj.done) {
            return;
        }
        result.push(elemObj.value);
    }
    yield result;
}

以上を組み合わせれば完成です。

完成

function* zip(...iterables) {
    const iterators = iterables.map(it => it[Symbol.iterator]());
    while (iterators) {
        const result = [];
        for (const it of iterators) {
            const elemObj = it.next();
            if (elemObj.done) {
                return;
            }
            result.push(elemObj.value);
        }
        yield result;
    }
}

使ってみます

配列・文字列・Setオブジェクト・Mapオブジェクトを、今回作ったzip()でイテレートしてみます。

const chars = ["a", "b", "c"];
const str = "qwe";
const numSet = new Set([1, 2, 3, 4, 5]);
const numMap = new Map([[1, 1], [2, 4], [3, 9], [4, 16]]);

for (const elems of zip(chars, str, numSet, numMap)) {
    console.log(elems);
}

// 出力
// Array(4) [ "a", "q", 1, Array [ 1, 1 ] ]
// Array(4) [ "b", "w", 2, Array [ 2, 4 ] ]
// Array(4) [ "c", "e", 3, Array [ 3, 9 ] ]

期待通りの動作です。


  1. この記事では、PythonのイテラブルオブジェクトとJavaScriptの反復可能オブジェクトをまとめて「イテラブル」と呼ぶことにします。 

  2. この記事では、JavaScriptの配列をPythonのリストに相当するものとして扱います。 

  3. JavaScriptのブラケット表記はプロパティアクセサーなので、numSet[1]のように書くと「numSetというオブジェクトの1というプロパティ」にアクセスすることはできます(通常このようなプロパティは未定義なのでundefinedになります)。しかし、普通はこれをSetオブジェクトの要素へのアクセスとは見做さないでしょう。 

  4. もしこのメソッドに馴染みのない方がおられましたら、MDNの反復処理プロトコルの記事をご覧ください。 

  5. it[Symbol.iterator]()です。it.Symbol.iterator()ではありませんので注意してください。 

  6. 変更不可能な配列として返すだけならyield Object.freeze(result)とすれば実現できますが、あまり意味はないと思います。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【jquery,js】連動するオブジェクトを実装する [val,change,radioボタン] [js11_20210304]

処理の概要

チェックボックスを変更すると、連動して別のチェックボックスにも同じ変更が施される。

処理のフロー:

今回は特に記述なし。

画面イメージ

画像1

画像2

ソースコード

index.html
<body>
       <div>
           【連動するチェックボックス】<br>
           同意する<input type="checkbox" id="interLockingCheck1"><br>
           会員になる<input type="checkbox" id="interLockingCheck2"><br>
       </div>
       <br><br>
       <div>
            【連動するラジオボタン】<br>
            子供<input type="radio" name="interLockingRadio1" value="1"><br>
            大人<input type="radio" name="interLockingRadio1" value="2"><br>
            <br>
            バス運賃(三ノ宮から神戸市北区):150円<input type="radio" name="interLockingRadio2" value="1"><br>
            バス運賃(三ノ宮から神戸市北区):300円<input type="radio" name="interLockingRadio2" value="2"><br>
        </div>
        <div id="sumValueOutput"></div>
</body>
main.js
$(function(){
    var sumValue = 0;

    $("#interLockingCheck1").click(function(){
        var lobjCheck = $("#interLockingCheck1").prop("checked");
        $("#interLockingCheck2").prop("checked",lobjCheck);
    });

    $("input[name=interLockingRadio1]").click(function(){
        var lobjRadio = $("input[name=interLockingRadio1]:checked").val();
        $("input[name=interLockingRadio2]").val([lobjRadio]);
    });

    $("input[name=interLockingRadio1]").change(function(){
        sumValue = 150 * $("input[name=interLockingRadio2]:checked").val();
        var lobjMessage = "バス運賃の合計は " + sumValue + " 円です。";
        $("#sumValueOutput").html(lobjMessage);
    });

});

ポイント

html:
なし
js:
(1)propは引数が1つの場合は、取得。引数が2つの場合は指定の属性を変更出来る
(2)チェックボックスから取得した値はすべて文字列なので、valに入力する場合は変換が必要。
(3)ラジオボタンで選択しているセレクタを選択する場合は「:checked」で選択できる。
(4)ラジオボタンのグルーピングは同じnameを付けること。

参考資料

JavaScript(仕事の現場でサッと使える!デザイン教科書) p60

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Netlifyを使って自分のプロフィールサイトを簡単に公開する

はじめに

何かプロフィールサイトを作って公開したいと思ったときに、方法はいくつかあると思います。
今回は、Netlifyを使って簡単に自分のプロフィールサイトを公開する方法について書いていきたいと思います。

環境

  • ArchLinux

Netlifyとは

Netlifyは、最新のWebプロジェクトを自動化するためのオールインワンプラットフォームです。ホスティングインフラストラクチャ、継続的インテグレーション、およびデプロイメントパイプラインを単一のワークフローに置き換えます。プロジェクトの成長に合わせて、サーバーレス機能、ユーザー認証、フォーム処理などの動的機能を統合します。

ドキュメントより抜粋

料金

個人的なプロジェクト、趣味のサイトなどは無料。使える機能としては

  • Gitからの自動ビルド
  • グローバルエッジネットワークにデプロイする
  • プッシュごとのサイトプレビュー
  • 任意のバージョンへの即時ロールバック
  • 静的アセットと動的サーバーレス機能をデプロイする

Netlify

手順

1. プロフィールサイトの作成

まずはプロフィールサイトを作らなければ始まりません。今回はReactのCreate React Appを使います。

yarn create react-app mypage
yarn start

qiita1.png
これがブラウザで表示されればok

※ 今回はあくまで公開する方法の紹介なので、サイト自体の作りは簡単に済ませちゃいます。

構造はこんな感じ
qiita2.png

App.js
import './App.css';
import profileimg from "./profileimg.png";

function App() {
    return (
        <div className="body">
            <div className="header">
                <h1>MyPage</h1>
            </div>

            <div className="main">
                <img src={profileimg} alt="profileimg" />
                <div className="text">
                    <h3>About</h3>
                    <p>Hello My Profile Page!<br></br>
                    Name:Taro<br></br>
                    Birthday:2021/03/04
                    </p>
                    <h3>Study</h3>
                    <p>JavaScript / HTML / CSS</p>
                </div>
            </div>

            <div className="footer">
                <p>Copyright ©</p>
            </div>
        </div>
    );
}

export default App;

App.css
.body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.header {
    padding: 10px;
    background-color: #dcdcdc;
}

.header h1 {
    margin-left: 40px;
}

img {
    border: 2px solid black;
    border-radius: 50%;
}

.main {
    flex: 1;
    display: flex;
    justify-content: center;
    align-items: center;
}

.main h3 {
    margin-bottom: 0px;
}

.text {
    margin: auto 40px auto 40px;
    display: flex;
    flex-direction: column;
}

.footer {
    padding: 10px;
    text-align: center;
    background-color: #dcdcdc;
}

こんな感じの画面が表示されていたらokです
qiita3.png

そしてビルドする

yarn build

新しくbuildというディレクトリができているはずです。

GitHubにリポジトリを作ってプッシュ

分かる人はとばしてもらってもいいですが、一応手順を書いておきます。
アカウント登録をしていない人は登録してもらって、している方はリポジトリを作ります。
この時、PublicにするかPrivateにするかはどちらでも大丈夫です。
今回はPublicで作成します。
qiita4.png

作成したときに手順はページに出てきますが

git init
git add .
git commit -m "first commit"
git branch -M main
git remote add origin {url}
git push -u origin main

今回はこれでプッシュ
この時注意しないといけないのは、今回yarnを使用しているためyarn.lockがあるディレクトリ内で行ってください。理由は後ほど説明します。

Netlifyで公開する

最後の公開の手順です。Netlifyにアカウント登録をしていない人はまずアカウント登録をしてください。
登録が終わったら画面にあるNew site from Gitをクリック
qiita5.png
すると下の画面が表示されるので、
qiita6.png
GitHubをクリック。
別ウィンドウが開かれると思います。
qiita7.png
この画面が出たら、自分のアカウントを選択。
qiita8.png
スクロールするとリポジトリを選択する部分があります。All repositoriesでもいいのですが、今回は作成したリポジトリだけにしたいのでOnly select repositoriesで作成したリポジトリを選択します。

そしてNetlifyの画面へ戻ります。画面が変わらないようであればページ更新してもらって、上記と同じ様にGitHubをクリックしてもらえれば先程選択したリポジトリが表示されていると思います。
そのリポジトリをクリックすると
qiita9.png
この画面が出てくればokです。
もしBuild CommandとPublish directoryが空白であれば画像と同じ様に入力してもらえれば大丈夫です。

ここで思い出してほしいのが、リポジトリにプッシュする時のこの文。

この時注意しないといけないのは、今回yarnを使用しているためyarn.lockがあるディレクトリ内で行ってください。理由は後ほど説明します。

これがここで関わってきます。もしyarn.lockがないディレクトリでプッシュしてしまっていると、Deploy siteをしたときにエラーがでて失敗します。

Yarnをローカルで使用してJavaScriptの依存関係をpackage.jsonファイルにインストールする場合、Yarnはyarn.lockインストールされたモジュール名とバージョンを記録するファイルを作成します。このファイルをリポジトリ内のサイトのベースディレクトリにコミットすると、Yarnがインストールyarnされ、コマンドを実行してyarn.lockファイルで指定された依存関係をインストールします。この動作は、NETLIFY_USE_YARN以下で説明する環境変数でオーバーライドできます。

ドキュメントより抜粋
自分も最初、command not foundといった文でエラーが出て失敗したのですが、yarn.lockがないとyarnがインストールされず、エラーになるみたいですね。

では以上を踏まえた上でDeploy siteをクリックします。
するとデプロイが始まるので終わるまで待ちます。
成功するとPreview deployに変わりますのでクリックすると、先ほど作成したページが表示されます。

URLの変更

初期のURLでは分かりづらいので、自分が指定したものに変えます。
(そのままでもいい人は飛ばしてください)
ホームに戻り、追加されているSitesに追加されたリンクをクリック。
Site settingsをクリックしてChange site nameをクリック。
すると入力欄が出てくるので、好きな名前を入力しましょう。
※ すでに使用されているものは設定不可

まとめ

以上で簡単ではありますが自分の作成したサイトをNetlifyを使って公開する方法でした。
今回作成したページ
https://optimistic-almeida-cd1568.netlify.app/

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GASでプログラミング入門 Vol.9

GASでプログラミング入門 Vol.9

社内サークルにてエンジニアから非エンジニアの方向けにプログラミングを教えるという活動を行っています。

今回はその教材第9弾です。
前回の記事はこちら

前回の演習問題の解答例

(1). 下記のような実行結果になるように連想配列を作成して下さい。
連想配列のキー名などは任意の名称をつけて下さい。

実行結果

ユーザーID:U0001
ユーザー名:鈴木一郎
メールアドレス:suzuki@example.com
電話番号:xxx-xxxx-xxxx

解答例コード

function myFunction(){
    let user = {id: "U0001", name: "鈴木一郎", mailaddress: "suzuki@example.com", tel: "xxx-xxxx-xxxx"};
    console.log("ユーザーID:" + user.id);
    console.log("ユーザー名:" + user.name);
    console.log("メールアドレス:" + user.mailaddress);
    console.log("電話番号:" + user.tel);
}

(2). 下記のような実行結果になるようにプログラムを作成して下さい。
連想配列は下記のコードを使用して下さい。

let persons = [
    {name: "鈴木一郎", age: 30, birthday: "1990/04/02", hobby: "ドライブ"},
    {name: "山田太郎", age: 20, birthday: "2000/08/30", hobby: "登山"},
    {name: "佐藤花子", age: 24, birthday: "1996/05/10", hobby: "ショッピング"},
    {name: "田中次郎", age: 27, birthday: "1993/07/20", hobby: "スノーボード"},
    {name: "杉本沙耶香", age: 32, birthday: "1988/02/23", hobby: "アロマセラピー"},
    {name: "市井由美子", age: 26, birthday: "1994/03/03", hobby: "漫画"}
];

実行結果

年齢が27才以上の人は下記の人たちです。
鈴木一郎さん、趣味はドライブ
田中次郎さん、趣味はスノーボード
杉本沙耶香さん、趣味はアロマセラピー
------------------------------
年齢が26才以下の人は下記の人たちです。
山田太郎さん、誕生日は2000/08/30
佐藤花子さん、誕生日は1996/05/10
市井由美子さん、誕生日は1994/03/03

解答例コード

function myFunction(){
    let persons = [
        {name: "鈴木一郎", age: 30, birthday: "1990/04/02", hobby: "ドライブ"},
        {name: "山田太郎", age: 20, birthday: "2000/08/30", hobby: "登山"},
        {name: "佐藤花子", age: 24, birthday: "1996/05/10", hobby: "ショッピング"},
        {name: "田中次郎", age: 27, birthday: "1993/07/20", hobby: "スノーボード"},
        {name: "杉本沙耶香", age: 32, birthday: "1988/02/23", hobby: "アロマセラピー"},
        {name: "市井由美子", age: 26, birthday: "1994/03/03", hobby: "漫画"}
    ];
    console.log("年齢が27才以上の人は下記の人たちです。");
    for(person of persons){
        if(27 <= person.age){
            console.log(person.name + "さん、趣味は" + person.hobby);
        }
    }
    console.log("------------------------------");
    console.log("年齢が26才以下の人は下記の人たちです。");
    for(person of persons){
        if(person.age <= 26){
            console.log(person.name + "さん、誕生日は" + person.birthday);
        }
    }
}

なお解答例はあくまで例なので、必ずしも上記のようになっていないといけないわけではありません。

nullについて

nullというのはオブジェクトの値が存在しないことを表す値です。
連想配列のようなオブジェクトの初期値などで用いることが多く、目的のオブジェクトが見つからなかったなどの状態を指す意味で用いられることがあります。
例えば連想配列の中から任意のデータを検索する際に、見つかった場合は見つかった連想配列のデータを、見つからない場合にはnullをというようにすれば、データが見つかったか見つからなかったかをプログラム内で判定することが可能になります。

let persons = [
    {name: "鈴木一郎", age: 30, birthday: "1990/04/02", hobby: "ドライブ"},
    {name: "山田太郎", age: 20, birthday: "2000/08/30", hobby: "登山"},
    {name: "佐藤花子", age: 24, birthday: "1996/05/10", hobby: "ショッピング"},
    {name: "田中次郎", age: 27, birthday: "1993/07/20", hobby: "スノーボード"},
    {name: "杉本沙耶香", age: 32, birthday: "1988/02/23", hobby: "アロマセラピー"},
    {name: "市井由美子", age: 26, birthday: "1994/03/03", hobby: "漫画"}
];
let search = null;
for(person of persons){
    if("田中次郎" == person.name){
        search = person;
    }
}
if(search == null){
    console.log("該当するユーザーが見つかりませんでした。");
} else {
    console.log("ユーザー名:" + search.name);
    console.log("年齢:" + search.age);
    console.log("誕生日:" + search.birthday);
    console.log("趣味:" + search.hobby);
}

break文

for文のような繰り返し処理を行っている最中に、途中で繰り返し処理を中断したいことがあります。
そのような場合に、breakというキーワードを使うと繰り返し処理を中断することが可能です。

let persons = [
    {name: "鈴木一郎", age: 30, birthday: "1990/04/02", hobby: "ドライブ"},
    {name: "山田太郎", age: 20, birthday: "2000/08/30", hobby: "登山"},
    {name: "佐藤花子", age: 24, birthday: "1996/05/10", hobby: "ショッピング"},
    {name: "田中次郎", age: 27, birthday: "1993/07/20", hobby: "スノーボード"},
    {name: "杉本沙耶香", age: 32, birthday: "1988/02/23", hobby: "アロマセラピー"},
    {name: "市井由美子", age: 26, birthday: "1994/03/03", hobby: "漫画"}
];
let search = null;
for(person of persons){
    console.log(person.name);
    if("田中次郎" == person.name){
        search = person;
        break;
    }
}
if(search == null){
    console.log("該当するユーザーが見つかりませんでした。");
} else {
    console.log("ユーザー名:" + search.name);
    console.log("年齢:" + search.age);
    console.log("誕生日:" + search.birthday);
    console.log("趣味:" + search.hobby);
}

上記コードを実行すると下記のようになります。

鈴木一郎
山田太郎
佐藤花子
田中次郎
ユーザー名:田中次郎
年齢:27
誕生日:1993/07/20
趣味:スノーボード

仮にbreakを書かなかった場合は下記になります。

鈴木一郎
山田太郎
佐藤花子
田中次郎
杉本沙耶香
市井由美子
ユーザー名:田中次郎
年齢:27
誕生日:1993/07/20
趣味:スノーボード

コード内では田中次郎を検索しているので、見つかった以降の杉本沙耶香市井由美子は処理する必要がないので、breakを書かなかった時に比べると繰り返し処理が2回余計に実行されてしまっています。
このように任意のデータを検索するような処理で、見つかった場合にそれ以降の処理を行う必要がないような時にはbreakを使用して中断することで、無駄な処理を行わずにすみます。

論理演算子

論理演算子とはif文などの条件式に記述する際に、複数の条件式を全て満たす、いずれか満たす、否定するなどの際に使用します。
論理演算についてはリンク先を参照してください。

論理積(and)

複数の条件を全て満たす時に使用します。
記述は条件式1 && 条件式2と記載します。
これは条件式1と条件式2をどちらも満たす時を表現しています。

for(person of persons){
    console.log(person.name);
    if("田中次郎" == person.name && 27 == person.age){
        // 名前が田中次郎でかつ年齢が27歳の時
        search = person;
        break;
    }
}

先ほどのコード例で言うと、例えば田中次郎さんと同姓同名の方がリスト内にいた場合に、どちらの田中次郎さんかを特定する為に、年齢も条件式に加えるなどを行いたい時に用います。

論理和(or)

複数の条件をいずれかを満たす時に使用します。
記述は条件式1 || 条件式2と記載します。
これは条件式1と条件式2をいずれかを満たす時を表現しています。

for(person of persons){
    console.log(person.name);
    if("田中次郎" == person.name || 27 == person.age){
        // 名前が田中次郎、もしくは年齢が27歳の時
        search = person;
    }
}

先ほどのコード例で言うと、例えば田中次郎さんと同い年の人も条件に引っ掛けたい時などに使用します。

否定(not)

条件を満たさない時に使用します。
記述は!条件式と記載します。
これは条件式を満たさない時を表現しています。

for(person of persons){
    console.log(person.name);
    if(!"田中次郎" == person.name){
        // 名前が田中次郎ではない時
        search = person;
    }
}

先ほどのコード例で言うと、例えば田中次郎さん以外の人を条件に引っ掛けたい時などに使用します。

演習問題

(1). 想定していた実行結果になるようにプログラムを修正して下さい。

function myFunction(){
    let persons = [
        {name: "鈴木一郎", age: 30, birthday: "1990/04/02", hobby: "ドライブ"},
        {name: "山田太郎", age: 20, birthday: "2000/08/30", hobby: "登山"},
        {name: "佐藤花子", age: 24, birthday: "1996/05/10", hobby: "ショッピング"},
        {name: "田中次郎", age: 27, birthday: "1993/07/20", hobby: "スノーボード"},
        {name: "杉本沙耶香", age: 32, birthday: "1988/02/23", hobby: "アロマセラピー"},
        {name: "市井由美子", age: 26, birthday: "1994/03/03", hobby: "漫画"},
        {name: "鈴木一郎", age: 20, birthday: "2000/04/19", hobby: "ボルダリング"}
    ];
    let search = null;
    for(person of persons){
        if("鈴木一郎" == person.name){
            search = person;
            break;
        }
    }
    if(search == null){
        console.log("該当するユーザーが見つかりませんでした。");
    } else {
        console.log("ユーザー名:" + search.name);
        console.log("年齢:" + search.age);
        console.log("誕生日:" + search.birthday);
        console.log("趣味:" + search.hobby);
    }
}

想定していた実行結果

ユーザー名:鈴木一郎
年齢:20
誕生日:2000/04/19
趣味:ボルダリング

実際の実行結果

ユーザー名:鈴木一郎
年齢:30
誕生日:1990/04/02
趣味:ドライブ

(2). 下記のような実行結果になるようにプログラムを作成して下さい。
連想配列は下記のコードを使用して下さい。

let persons = [
    {name: "鈴木一郎", age: 30, birthday: "1990/04/02", hobby: "ドライブ"},
    {name: "山田太郎", age: 20, birthday: "2000/08/30", hobby: "登山"},
    {name: "佐藤花子", age: 24, birthday: "1996/05/10", hobby: "ショッピング"},
    {name: "田中次郎", age: 27, birthday: "1993/07/20", hobby: "スノーボード"},
    {name: "杉本沙耶香", age: 32, birthday: "1988/02/23", hobby: "アロマセラピー"},
    {name: "市井由美子", age: 26, birthday: "1994/03/03", hobby: "漫画"},
    {name: "鈴木一郎", age: 20, birthday: "2000/04/19", hobby: "ボルダリング"}
];

実行結果

personsに含まれる人数は7人です。
personsの平均年齢は25.571428571428573才です。

まとめ

いかがでしたでしょうか。
今回はnull、break文、論理演算子について紹介しました。
今回紹介した例ではリストデータの中から任意のデータを検索するなどの処理で使用されることが多いので、どのような目的でどんな風に使用されていたかをしっかりと理解しておくと良いと思います。
それではまた次の記事でお会いしましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jQueryフォーム追加

$("#add").click(function(){
        var cloneElement = $('.className:last-child').clone(true);
        $('.className').parent().append(cloneElement);
});

これだけ。IDとかもクローンしてしまうから、そこは気をつけなはれや。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自作のジオメトリでThreeBSPを使う際に気をつけたいこと

ThreeBSPを使ってみたが結構手こずったのでこれから使いたい人に向けて記事を書こうと思います。

対象

ThreeBSPを使う方、THREE.jsでブーリアン演算をしたい人

1. THREE.jsのバージョン

ThreeBSPは新しいTHREE.jsのバージョンでは動きませんでした。
r89くらいでは動作しました。
多分MeshのGeometryの形式が違う

2. BufferGeometryは使えない

自作のジオメトリを使いたいときパフォーマンス的にGeometryではなく、
BufferGeometryを使うことが推奨されていますが(というか最新バージョンではGeometryは使えなくなってしまっていますが)、BufferGeometryではエラーが出ました。
これはTHREE.js側の問題ですが、BufferGeometryをGeometryに変換するのもうまくいきませんでした。

3. 面がちゃんと閉じられていない

面がちゃんと閉じられていないとエラーは出ませんが、意図しない結果になります。
おかしなことになった人は見直してみてください。

最後に

ThreeBSPについて調べても記事があまり出てこなかったので苦労しました。
私と同じようにThreeBSPがうまく行かなかった人の参考になれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WebRTC(SkyWay)でステレオ配信テストやってみた

はじめに

WebRTC Platform SkyWayのプリセールスエンジニア、サポートエンジニアのBBこと馬場です。
たまにお客様からステレオ配信がうまく行かない(モノラルになる)というお問い合わせをいただきます。
WebRTC(SkyWay)でステレオ配信やるときには、いくつか注意点があるので、以前行った試験結果をまとめたものを公開します。

なんでモノラルになっちゃうの?

この原因は二つありました。

原因1: Chromeのバグ(?), 仕様(?)問題

Chromeでは gUMConstraintsechoCancellation=true がデフォルトになっているんですが、これがステレオに対応していないらしいです。
- Issue 8133: OPUS stereo audio over RTP is muxed to mono

echoCancellation=false にして、opusのfmtpパラメータに stereo=1 を追加するといいよとのこと。

原因2: SkyWayのSFUの仕様問題

原因1 についてはFirefoxで受ければステレオで再生できるのですが、SkyWayのSFUはステレオに対応していません。SFUでモノラルにMIXされます。そのため、ブラウザに依らずステレオ音源をそのまま配信することができません。

ステレオ配信するために試してみたことと結果

観点

  • echoCancellation=false は有効か
  • Firefoxを使えば問題ないのか
  • sfuを使うとどうなるのか
  • LR音源を複数トラックに分けて、受信側でステレオMIXするやり方は有効か
観点 配信元 配信先 room その他 結果
ベース ChromeM86 ChromeM86 mesh 何もせずそのまま ×モノラル
ベース ChromeM86 ChromeM86 sfu 何もせずそのまま ×モノラル
echoCancellation=false は有効か ChromeM86 ChromeM86 mesh echoCancellation=false わからず
Firefoxを使えば問題ないのか ChromeM86 Firefox68 mesh 何もせずそのまま ○ステレオ
sfuを使うとどうなるのか ChromeM86 Firefox68 SFU 何もせずそのまま ×モノラル
LR音源を複数トラックに分けて配信 ChromeM86 ChromeM86 mesh LRの音源を複数のMediaStreamTrackに分けて送信して、受信側でステレオMIX ○ステレオ

検証内容詳細

自宅にステレオマイクがなかったので、ステレオ音源を <audio> で再生してAudioContextのAPIを使って MediaStream として出力して使いました。
なお、WebRTCの部分はSkyWayのJavaScript SDKを使って実装しています。

Code
const localAudioFile = document.getElementById('js-local-audiofile');
const localStream = await getStereoStream(); 
function getStereoStream(){
  const audioCtx = new(window.AudioContext || window.webkitAudioContext);
  const source = audioCtx.createMediaElementSource(localAudioFile);
  const destination = audioCtx.createMediaStreamDestination();
  source.connect(destination);
  localAudioFile.play();
  return destination.stream;
}

にセットされた音源を、AudioContext.createMediaElementSource() に入力して、AudioContext.createMediaStreamDestination() で作成した出力ノードに接続して、MediaStream として取り出すという感じ。
※ ユーザ操作によるイベント配下におかないと再生されないので注意。
これをそのまま peer.joinRoom() のStreamに渡してやればOKです。

音源は http://www.gatelink.co.jp/hw/etc/audiotest/index.html からお借りしました。

echoCancellation=false は有効か

getUserMediaでステレオ入力がモノラルに変換されてしまう現象の回避@yakan10 によると、echoCancellation: false とすればステレオで再生できるで!と書いていたので、試してみたかったのですが。。。ステレオマイク内からできへんやん!

一応、MediaStreamTrack.applyConstraints(constraints)echoCancellation: false を適用してやってみましたが、やはり、OverconstrainedError がでて反映できずでした。
ステレオマイクないとだめですね。

Error
OverconstrainedError {name: "OverconstrainedError", message: "Cannot satisfy constraints", constraint: ""}
constraint: ""
message: "Cannot satisfy constraints"
name: "OverconstrainedError"
__proto__: OverconstrainedError
Code
const constraints = {
  echoCancellation: false
};

await localStream.getAudioTracks().forEach((track) => {
  track.applyConstraints(constraints)
  .catch(console.error);
});

結論: echoCancellation=false が有効かはステレオマイクがないため検証できませんでした!!!

Firefoxを使えば問題ないのか

この問題は、ChromeとSafariでは確認されているもののFirefoxでは大丈夫とのことだったので、配信先をFirefoxで検証してみました。

  • 配信元: ChromeM86
  • 配信先: Firefox68
  • room: Mesh
  • そのほかはサンプルのまま

問題なく、ステレオで再生されました。

結論: 配信先がFirefoxであればステレオ再生可能

SkyWayのSFUを使うとどうなるのか

先ほどステレオ配信がうまくいった 配信先: Firefox でRoomのモードをSFUにして検証。

  • 配信元: ChromeM86
  • 配信先: Firefox68
  • room: SFU
  • そのほかはサンプルのまま

想定どおりモノラルMIXされて再生されました。

結論: SFUを使うとモノラル再生になる。

LR音源を複数トラックに分けて、受信側でステレオMIXするやり方は有効か

最後に、ステレオ音源そのまま投げてだめなら、マルチストリームでLRの音源を別で送って、受信側でステレオMIXすればいいんじゃない?ということで検証。
配信元にてステレオ音源を audioCtx.createChannelSplitter 使って、それぞれ別のMediaStreamTrackに分けてMediaStreamに乗せて送信し、配信先で audioCtx.createChannelMerger で2chにマージして再生してみました。

Code(配信側)
    const localStream = new MediaStream();
    const localStreamTracks = await getStereoStreamTrack();
    function getStereoStreamTrack(){
      const audioCtx = new(window.AudioContext || window.webkitAudioContext);
      const source = audioCtx.createMediaElementSource(localAudioFile);
      const destinationL = audioCtx.createMediaStreamDestination();
      const destinationR = audioCtx.createMediaStreamDestination();
      const splitter = audioCtx.createChannelSplitter(2);
      source.connect(splitter);
      splitter.connect(destinationL, 0);
      splitter.connect(destinationR, 1);
      localAudioFile.play();
      return [destinationL.stream, destinationR.stream];
    }
    localStream.addTrack(localStreamTracks[0].getTracks()[0]);
    localStream.addTrack(localStreamTracks[1].getTracks()[0]);
Code(受信側)
    room.on('stream', async stream => {
      const audioCtx = new(window.AudioContext || window.webkitAudioContext);
      const dest = audioCtx.createMediaStreamDestination();
      const merger = audioCtx.createChannelMerger(stream.getTracks().length);
      stream.getTracks().forEach((track, index) => {
        const tmpStream = new MediaStream([track]);
        const mutedAudio = new Audio();
        mutedAudio.muted = true;
        mutedAudio.srcObject = tmpStream;
        mutedAudio.play();
        const source = audioCtx.createMediaStreamSource(tmpStream);
        source.connect(merger, 0, index);
      });
      merger.connect(dest);

      const newAudio = document.createElement('audio');
      await newAudio.play().catch(console.error);
    });

Chromeでもステレオで再生されました!

環境は
- 配信元: ChromeM86
- 配信先: ChromeM86
- room: mesh
- LR音源を複数トラックに分けて、受信側でステレオMIX

結論: LR音源を複数トラックに分けて、受信側でステレオMIXする方法は有効!!!

ただし...
SkyWayはマルチストリームに完全には対応していないので注意が必要です。
SFURoomではだめだったり、あとからRoomに入ってきた人は片方しか受け取れなかったりします。
なので、これもかなり限定的な用途でしか使えませんね。
参考) FAQ: マルチストリームに対応していますか?

多分、PeerConnection自体分けて送って合成すればいけるとは思います。

最後に

SkyWayでステレオ配信をするのであればやり方は下記2つくらいが考えられるかなぁという感じです。

  1. MediaConnectionを使って音声だけ配信先にMeshにつないで、映像も必要なら別途Peerを作成してSFURoomで配信する
  2. SFURoomを複数作成して、それぞれにLRの音源を送って、受信側でステレオMIXする。

配信規模的には2のほうが良さそうですが、Room分けちゃうので左右の遅延値のズレがどの程度でるかがきになるところ。
この点に関しては、別途、検証することがあれば公開します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コールバック関数

// これがコールバック関数
function greeting(name) {
  alert('Hello ' + name);
}

// 2. callbackという仮引数名でgreeting関数を受け取る
function processUserInput(callback) {

  // 3. 入力フォームを表示させ、入力値を変数に代入
  let name = prompt('Please enter your name.');

  // 4. 3の変数を引数に渡してコールバック関数実行
  callback(name);
}

// 1. greeting関数を引数で渡す
processUserInput(greeting);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

共有タイマーでクライアントとサーバの役割分担を学ぶ

概要

この記事は、既存のアプリケーションを改修して、複数人で使えるようにした際の記録です。

主に、クライアント側とサーバ側のデータの流れをどのようにしたら良いか、というようなシステム構成部分の思考をまとめた内容になっています。

何を作ったか?

今回作った成果物は、ブラウザ上で動くカウントダウンタイマーです。
これは、アジェンダとそれにかかる時間を入力することで、円グラフを模したタイマーを表示します。
このタイマーは、色分けされたアジェンダごとに使える時間を表示し、「全体でどの程度時間がたって今何のアジェンダが進行しているのか?」を可視化することができます。
IMG_4431.jpg

機能一覧

  • タイマーをスタート出来る
  • タイマーをリセット出来る
  • 予定されているアジェンダと時間の進捗を確認出来る
  • アジェンダを読み取ることが出来る
    • 読み取り方法
      1. テキストからの入力出来る
      2. 写真からの読み取ることが出来る
  • アジェンダを更新することが出来る
    • タイマー起動中にアジェンダを更新すると時間が追加出来る
  • アジェンダを削除することが出来る
  • 完了したアジェンダをチェックすると残り時間が変更する

現状は?

このカウントダウンタイマーはスマホのブラウザからの利用を想定しており、PCの余計なタブを開かずに、スマホとPCを見比べることで進捗を把握することができるようになっています。

さて、実際のシステム内部の仕組みですが、、
これは、ほぼクライアント側のみの実装になっていて、時間の経過やアジェンダは、各PC側で保持しています。つまり、イベントの進捗状況をを参加者間で共有することが出来ない状態。

アプリケーション自体はNode-REDを用いて実装しており、現状のシステムは次のような流れで動作しています。ここでのサーバ側の役割は、Formから受け取ったアジェンダと時間を整形して、HTMLに渡すくらいの仕事しかしていません。

  1. Formにアジェンダを含めてPOST
  2. Node-REDでFormを受け取って、アジェンダ部分をHTMLにレンダリング
  3. JavascriptでHTMLの要素を取得して、chart.jsを使って円グラフ形式のタイマーを表示

【仕事の分担】
スクリーンショット 2021-03-04 9.58.11.png

どうなっていたいのか?

イベント(や会議)の進捗状況が分かるようになるといっても、それはこのアプリの使用者のみで、時間を意識することができるのはせいぜいファシリテーターくらい。参加者全員が時間を意識することが出来れば、よりイベントや会議を有意義な時間と考えています。

経過時間を共有出来ることのメリット

  • オンライン会議において参加者全員が時間を意識することが出来る。時間が押していたら、他の参加者も予定通りに進めようという気になる。
  • オンライン会議は場所の制約がないため予定時間をオーバーしてしまいがちになる。
  • 発表者が変わるような会議の際には、全体の会議時間の、自分の持ち時間を意識することが出来る
  • 全員が時間を見えることで、ブレストなどでは無言の時間を減らそうという気になり議論が活発化する。

つまり今回のカウントダウンタイマーを次のような状態にしたいです。

  1. 他の参加者にも見えるようにして、会議の進捗を全員で共有できる
  2. 会議室ごとにタイマーを動作できる(チャットルームのような)

どうやって実現するか?

データをクライアント毎に持っていると、参加者同士が同じものを見れないということなので、時間を共有するためには、サーバ側にもう少し仕事させなきゃいけないようです。

「Webページのルーティング」と「POSTされたデータの整形」くらいしかしてなかったサーバ側に、「データの保持」という役割も持たせてあげれば、やりたいことが実現できそう。
つまり、情報自体はサーバ側に持たせて、クライアント側ではその情報を表示させるだけの状態にする。

ここでサーバに持たせる情報を、先ほどの画像から「時間」や「アジェンダ」とすると、、
パッと思いついた実現方法が二つ。

  1. クライアントがサーバにデータを取りに行く
  2. サーバからクライアントにデータをプッシュ

1.クライアントがサーバにデータを取りに行く

スクリーンショット 2021-03-04 10.01.03.png

時間をサーバ側に持たせて、クライアントはそれを都度取りに行く方法
具体的には以下のような流れで進める
1. 会議スタート!
2. 「会議のトータル時間」をサーバに送る
3. サーバ側で「経過時間」のカウント開始
4. サーバは「会議のトータル時間」から「経過時間」を引いて「残り時間」を計算
5. 1秒毎にクライアントはサーバにリクエストし、「残り時間」を画面に表示

2.サーバからクライアントにデータをプッシュ

これは先ほどの案の逆で、時間をサーバ側に持たせて、サーバからそれをクライアントに投げる方法。サーバ主導でメッセージを送信する為に、双方向通信を実装する必要がある。
1. 会議スタート!
2. 「会議のトータル時間」をサーバに送る
3. サーバ側で「経過時間」のカウント開始
4. サーバは「会議のトータル時間」から「経過時間」を引いて「残り時間」を計算
5. サーバはクライアント(参加者)を識別して時間をプッシュ。

懸念

二つの案を検討して、まず感じたのは、「タイマーという特徴がネック?」ということ。
時間というデータをサーバ上に保持しておくと、それをクライアントに伝えるために1秒毎に通信しなければいけなくなる。しかも、1秒毎に通信する割に情報自体は「経過時間」というそこまで重みのないもの。(やっていることはNTPっぽい?)初学者の自分でも、この構成はスマートじゃないということは、なんとなく理解出来る。また、案2は通信するクライアントを識別する必要もあるので、案1よりももっと考えることが多くなりそう。

【第3の案】時間の差分を使う

上の懸念点から学んだのは、「クライアント側に保持しているデータをごっそりサーバ側に移すとなんとなくよろしくない状態になる、、」ということ。
なので、もう一度思案して、クライアントとサーバ双方の仕事の分担を見直してみました。
スクリーンショット 2021-03-04 9.58.22.png

時間はもうクライアント側で計測をお願いしちゃいます。
その分サーバには別の仕事を割り振りました。この仕事はデータの保持になり、データは「会議室(ルーム )」に紐づいているアジェンダとそれがスタートされた時間になります。
そして同時に、このタイマーの利用者を『ルームの作成者(親)』『ルームの参加者(子)』という立場に分け、立場ごとに出来ることの違いを持たせる仕組みも作りました。
「タイマーの利用者ごとの出来ることの違い」と「クライアントとサーバの役割分担」によって出来た流れが以下のようになります。
1. 親がルームを作成する
2. 親がタイマーをスタートする
3. 子がルームに入る
4. 子はスタートされた時間と今の時間の差分だけタイマーを進める

親と子の時間の差分でタイマーを進めることで、クライアントとサーバの通信を一回だけにおさめることが可能になり、尚且つ今回やりたかったことが実現出来ています。

スクリーンショット 2021-03-04 12.05.29.png
スクリーンショット 2021-03-04 12.09.25.png

以下詳細な流れ
ルーム作成者(親)
1. Formにアジェンダを含めてPOST
2. Node-REDでFormを受け取って、アジェンダ部分をHTMLにレンダリング
3. roomIdを発行してアジェンダと紐付けてflow変数に格納しておく
4. JavascriptでHTMLの要素を取得して、chart.jsを使って円グラフ形式のタイマーを表示

{
    "roomId":"od1isga90vo",
    "startTime":"",
    "agendaList":["12分","aaa"]
}

ここでルーム作成者が会議のスタートボタンを押すと、startTimeに現在の時刻を格納する

{
    "roomId":"od1isga90vo",
    "startTime":"2021-02-25T05:58:30.186Z",
    "agendaList":["12分","aaa"]
}

その後、top画面に進行中のルームとしてroomIdが映し出される。

参加者(子)
1. ルーム作成者からrooIdを教えてもらい、ルーム選択画面に表示されているroomIdをクリック
2. 選択したroomIdを元に、紐づいているstartTimeとagendaListを参照
3. agendaListを元に、アジェンダ部分をHTMLにレンダリング
4. JavascriptでHTMLの要素を取得して、chart.jsを使って円グラフ形式のタイマーを表示
5. 参加者(子)がタイマーを表示させた現在時刻とstartTimeの差分を計算して、経過した時間を進める

まとめ

第3の案の実装時点で、「参加者間が時間を共有出来る」「会議室ごとにタイマーを起動できる」「クライアントとサーバの通信は出来るだけ少なめに」を3点を達成出来ていますが、これだけだと参加者が何かしら操作をした時に変更が全員に反映されません。例えば、「タイマーのリセットボタンを押したとき」や「アジェンダを変更した時」。
これを解消するためには、第2の案で出た双方向通信を使って、参加者のクライアント側の変更をサーバを介して全員に通知する必要がありそうなので、今後検討していきたいです。

今回はクライアントサーバシステムの役割分担を色々と検討してみましたが、タイマーというアプリというのもあって、構成を考える良い勉強になったと思います。システムの設計をあまり考えずに、今回のカウントダウンタイマーを作成したのもあって、少し強引さがあったと思います。事前設計の重要性も痛感したので、次回に開発に生かしていきたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

マトリックス風な画面作ってみる

現実逃避をかねてちょっと遊んでみたら意外と楽しかったので書く

HTML

<!DOCTYPE html>
<html lang="ja"">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Matrix text</title>
<script type="text/javascript" src="./matrix.js"></script>
</head>
<body>
<canvas id="q"></canvas>    
</body>
</html>

js

window.onload = Matrix = () => {
  // canvas領域のサイズを画面サイズに設定する
  const screen = window.screen;
  const width = (q.width = screen.width);
  const height = (q.height = screen.height);

  // 文字を256文字の座標を1で初期化する
  let letters = Array(256).join(1).split("");

  const draw = () => {
    // canvasのコンテキストを取得する
    const ctx = q.getContext("2d");

    // 黒でcanvas領域を塗りつぶす(alpha 0.12)
    // 半透明の黒で塗りつぶすので、前回書き込まれた文字は薄暗くなるものの、1回の描画では真っ黒にならない
    // 塗りつぶしの色を設定
    ctx.fillStyle = "rgba(0, 0, 0, .09";
    // 矩形領域を塗りつぶす
    ctx.fillRect(0, 0, width, height);
    // 文字の描画色を設定する
    ctx.fillStyle = "#0F0";

    // 1文字ずつ描画処理を行う
    letters.map(
      (textRender = (y, index) => {
        // 0xfeff0021 - feff007e (半角の英数記号)の範囲の文字コードを取得する(UTF-16)
        let code = 0xfeff0021 + Math.random() * 93;
        // 指定文字コードの文字を取得する(UTF-16の文字コードを指定する)
        text = String.fromCharCode(code);
        x = index * 10;
        // テキストをcanvas領域に描画する
        ctx.fillText(text, x, y);
        // 文字のy座標を下に移動する。yが700+ランダム値を超えたらyをリセットする。
        letters[index] = y > 700 + Math.random() * 10000 ? 0 : y + 10;
      })
    );
  };
  setInterval(draw, 30);
};

実際に動かしたらこんな感じ

See the Pen VwmdRbZ by すずけん (@suzuken1023) on CodePen.

参考記事

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ClubHouseに対抗してCryptoHouseを作ってみた

流行に乗り遅れること約一ヶ月
ようやくクラブハウスに招待してもらいました。

面白いけど、私ただのしがないwebエンジニア、自分から話すことがない。
ただの進化系ラジオじゃん、、
自分から手を上げて話してリスナーを満足させる?
無理無理!

仮想通貨とか最近また盛り上がってるからそういう系の話
聞いてみても面白いんだが、やはり自分から発言することはない。

誰か自分の代わりに面白いこと自動で話してくれないかなー?

でもみんな仕事中とかでも意外と耳は空いてるんだなー

ん?待てよ?仕事中こっそり見てる仮想通貨の価格
見ないで聞けばいいんじゃね?

ということで
作ってみました。

CryptoHouse

ここでちょっと比較してみましょう。

ClubHouse と CryptoHouse の比較

ClubHouse CryptoHouse
招待 必須 不要
OS iOSのみ 全デバイス
インストール 必要 不要
言語 英語 日本語(近日英語対応予定)
身バレリスク high 0%
有益性 人によりけり 人によりけり

思ってたんとちゃうかった人スミマセン(土下座)
仮想通貨の価格をただ読み上げてくれるだけのサイトです。
ClubHouseにまだ招待されていない方は
是非こちらで淡々と価格を聞いて資産を増やしてみてはいかがですか?

スクリーンショット 2021-03-04 9.38.47.png

できること

  • 一定間隔で指定した仮想通貨の価格を読み上げます。
  • 通貨ごとにしきい値を設定してその数値を超えたときだけお知らせします。

以上

ちょっとだけ技術の話

Vuetifyのsliderとかマジ便利(語彙力orz)

モバイルでいちいち細かい数字を入力するの面倒なので
スライダーを自作しようと思ったのですがサンプルほぼそのままで
実装できました。(テキスト入力もできます)
https://vuetifyjs.com/en/components/sliders/

SpeechSynthesisUtterance

という実験的な機能を使ってるので
ブラウザ環境によっては音が出ないものがあるようです。
参考にさせてもらった
https://qiita.com/hmmrjn/items/be29c62ba4e4a02d305c

fitty

地味すぎて気づかれないかもしれませんが
価格を大きく表示するために自動でフォントサイズを調整してくれるjsです。
ちょっと癖があるんであとからCSSで再度調整したりはしてます。
https://github.com/rikschennink/fitty

最後に

ニュースの読み上げとかはまだまだ難しそうな
SpeechSynthesisUtteranceですが
このくらいの需要であれば十分使える気がします。

まだまだ改善の余地はあるんですが
自己満足と、自分需要は満たしたので一旦これでリリース
反響次第で追加開発がんばります。

よかったらツイッターで絡んでください!
https://twitter.com/FxSlack

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React/Vue】なぜコンポーネントベースでWebアプリを作るのか

React/Vue などを使ってフロントエンドを開発する際、基本はコンポーネントベースで設計・開発すると思います。
なぜコンポーネントベースなのかどういうメリット・目的があるのか、今一度確認する機会があったので記事にします。
新入社員に「ここってどうしてコンポーネントに分けるんですか?」と聞かれた時に答えられるようにしておきましょう。

コンポーネントベース以外経験が無い方へ
Web アプリの学習を始めて間も無い方の中には、そもそもコンポーネントベース以外経験がない方も居るかもしれません。
そんな方は極端な例ですが、Web アプリの画面がもし1画面1コンポーネントで作られていた場合小さなコンポーネントを積み上げて作られていた場合を想像し、以下の記事では後者のメリットが書かれていると思って読んでみてください。

コンポーネントが持つべき 5 つの特徴

はじめにコンポーネントのあるべき姿ついて確認しておきます。

  1. 単一責任の原則
    コンポーネントが責任を持つ問題は 1 つにするべきです。
    ソースがシンプルになることで可読性が向上します。
    また、ソース変更時の影響範囲が明確になることで保守性が向上します。

  2. カプセル化されている
    コンポーネントを使いたいとき、インターフェースだけ知っていれば内部の実装を気にしなくて良いです。

  3. 置換可能である
    インターフェースさえ同じであれば違うコンポーネントに差し替えることができます。

  4. 再利用可能である
    再利用可能なコンポーネントは、そのコンポーネントが担っている責務に対して過不足なく機能を提供していると言えます。
    「過不足なく」というのは意外と難しく、特にありがちなのが機能を過剰につけてしまうことです。
    ある画面の文脈では必要な機能でも、別の画面では邪魔になることもあります。
    コンポーネントの責務を常に意識し、再利用性を損なうことがないようにすることが大切です

  5. 組み合わせて別の大きなコンポーネントを作成可能である
    再利用性が十分に高いことの裏付けです。
    一つ一つのコンポーネントは小さな問題しか解決できませんが、大きな問題を小さな問題に分割し、適切なコンポーネントに振り分ける役割を持つコンポーネントを作れば、それ自体が大きな問題を解決するコンポーネントになります。

コンポーネントベースのメリット

先に紹介した 5 つの特徴を持つコンポーネントで Web アプリを構成すると以下のようなメリットがあります。

1.複雑な UI も確実に組み立てられる

コンポーネントベース最大のメリットは複雑な UI を確実・堅牢に組み立てられることです。

堅牢な UI 開発を実現する

多くの機能を提供するアプリでは複雑な UI 設計が求められます。
例として Facebook の Web アプリを見てみると、ヘッダー・サイドメニュー・投稿記事リスト・広告など沢山の UI が並んでいます。
これらの UI がもし互いに依存しているような作りになっていた場合、意図せずバグを産んでしまうことがあります。
(ある UI の機能を変更したら別の UI が動かなくなった、ある UI のレイアウトを変更したら別の UI のレイアウトが崩れてしまった、など)
コンポーネント化された UI が個別で不具合なく動作することがテストされていれば、それを正しく使ったアプリの品質も保証できます

コンポーネント単位でテストできる

アプリの品質を担保するための最も重要なポイントです
UI はアプリに組み込まれるとアプリ全体の状態に左右されてしまうことが多いので、単体でテストすることが難しいです。
コンポーネント化して独立した UI であればアプリの状態に依存せず単体でテスト可能です
適切に分割された小さな実装であれば必要なテストケースも少なく、テスト項目も作りやすいです。

不具合のリスクポイントを減らすことができる

一般的に書くコードの量が減ればバグの量も減ります
再利用可能なコンポーネントを作成することで全体のコード記述量を減らすことができます。

メンテナンスがしやすくなる

UI に変更を加えた際に影響を受ける範囲がコンポーネント内に留まるので、メンテナンスがしやすいです
見た目だけを責務とするコンポーネントであれば、修正作業はエンジニアではなくデザイナーが直接行うこともできます。

解決する問題を小さくすることで不具合発生リスクを減らす

複雑なコードより簡単なコードの方が不具合発生リスクは低いです
一つ一つのコンポーネントが単純な問題を解決するために作られているのであれば、内容は簡単になり実装の難易度も低くなります。

2.開発作業を効率化する

コンポーネントベースは品質を担保するとともに開発速度向上にも繋がります。

再利用で実装量を減らす

再利用しやすいコンポーネントを作ることは開発速度向上に直結します

並行開発で待ち時間を減らす

画面単位の開発ではなくコンポーネント単位の開発だと、1画面に必要な UI を複数人で並行開発することも可能です
各コンポーネントは独立しているため、他のタスクに依存せず開発可能です。

仕様変更による手戻り作業を効率化する

画面単位で UI 開発していた場合、画面仕様に変更があれば必ず手戻りが発生します。
小さなコンポーネントを積み上げて大きな部品を開発する方式だと、仕様変更によって影響を受けるコードは比較的少なくなります
また大きな部品ほど後から作られるので、仕様変更のタイミングでまだ画面自体の実装に着手していない可能性もあり、その場合手戻りは無くなることもあります。

新規参入開発メンバーを最短で戦力化する

コンポーネント内で使用する技術スタックだけ知っていれば開発可能なので、サービス背景についての知識は概要だけで良い場合もあります。

複数のテスト・アプローチでテスト工数を下げる

コンポーネントがアプリに依存しない環境で単体実行できるので、さまざまなアプローチでのテストが可能です。
画面に依存している UI ではテストしづらいケースを時間をかけて再現し、何とかテストするということもしばしばありますが、独立したコンポーネントだと単独のテストなら非常に簡単に行うことができます。

複数アプリケーションの開発を容易にする

UI コンポーネントが部品としてアプリから分離できる形になっていれば、別のアプリでコードを再利用することが可能です
仕組み次第では複数アプリから同じコードを参照して、全体のメンテナンスコストを下げることもできます
ただしコードを変更したときの影響範囲がかなり大きくなるので、より堅牢な運用が必要となります。

3.ユーザーメリット

ここまではアプリを開発するエンジニアが受けるメリットの紹介でしたが、アプリを使うユーザーにとってもメリットがあります。

多機能アプリのユーザービリティ向上

アプリが多機能になるとたくさんの UI を画面に表示するため複雑になります。
ある UI コンポーネントが別の画面で使われていたとしても、ユーザーはすでにその UI コンポーネントの使い方を知っているため、すぐにその機能を使い始めることができます

まとめ

コンポーネントが持つべき 5 つの特徴

  1. 単一責任の原則
  2. カプセル化されている
  3. 置換可能である
  4. 再利用可能である
  5. 組み合わせて別の大きなコンポーネントを作成可能である


コンポーネントベースのメリット

1.複雑な UI も確実に組み立てられる

  • 堅牢な UI 開発を実現する
  • コンポーネント単位でテストできる
  • 不具合のリスクポイントを減らすことができる
  • メンテナンスがしやすくなる
  • 解決する問題を小さくすることで不具合発生リスクを減らす


2.開発作業を効率化する

  • 再利用で実装量を減らす
  • 並行開発で待ち時間を減らす
  • 仕様変更による手戻り作業を効率化する
  • 新規参入開発メンバーを最短で戦力化する
  • 複数のテスト・アプローチでテスト工数を下げる
  • 複数アプリケーションの開発を容易にする


3.ユーザーメリット

  • 多機能アプリのユーザービリティ向上


コンポーネント設計をする際にはこれらを意識することでより良い設計ができるかもしれません。
最後に注意点ですが、現実問題としてWeb アプリは必ずしもこの記事で紹介したようなコンポーネントだけで作られるわけではありません
例えばAtomicDesignで言うところの Pages は、実際のコンテンツに影響されるためカプセル化はできず、さらに再利用もできません
React/Vue で書くソース全てが上記の目的を持って書かれるわけではないことは頭に入れておきましょう。

読んでいただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

サイドバーのカレント表示で詰まったことと解決策

(備忘録なので少々煩雑です。落ち着いてきたら整理したい)

困った

hogehoge.hbs
<ul class="side-menu">
    <!-- ここからカレント表示したいのに・・・ --->
    <li id="side-menu"><a href="#gohan">{{oishiina}}</a></li>

        ...

    <li id="side-menu"><a href="#panmo">{{daisuki}}</a></li>
    <li id="side-menu"><a href="#pasta">{{mogumogu}}</a>
        <ul class="side-menu-child">
            {{#each this.shushoku as | koubutu | }}
            <li id='side-menu-child'>
                <!--ここ以降だけカレント表示で色がつく.なんで?? -->
                <a href='#{{koubutu.link}}'>{{koubutu.namae}}</a>
            </li>
            {{/each}}
        </ul>
    </li>
</ul>

<script>
    function changeBackColor(secNum) {
    if (secNum != current) {
        current = secNum;
        let secNum2 = secNum + 1;
        $('#side-menu li').removeClass('colorOn');
        $('#side-menu li:nth-child(' + secNum2 + ')').addClass('colorOn');
    }
</script>

原因

つまり上の書き方だと、「side-menuというidがついたタグ(ここではli)の子要素としてあるliタグ(id='side-menu-child'であるli)」にcolorOnクラスが追加されていたのです。

対応策

以下のように実装を変更すると、うまくサイドバーの最初からカレント表示されました。

newHogehoge.hbs
<ul class="side-menu">
    <li id="side-menu"><a href="#gohan">{{oishiina}}</a></li>

        ...

    <li id="side-menu"><a href="#panmo">{{daisuki}}</a></li>
    <li id="side-menu"><a href="#pasta">{{mogumogu}}</a>
        <ul class="side-menu-child">
            {{#each this.shushoku as | koubutu | }}
            <li id='side-menu-child'>
                <a href='#{{koubutu.link}}'>{{koubutu.namae}}</a>   
            </li>
            {{/each}}
        </ul>
    </li>
</ul>

<script>
    function changeBackColor(secNum) {
    if (secNum != current) {
        current = secNum;
        let secNum2 = secNum + 1;
        if (secNum2 <= 5) {
            $('ul #side-menu-child').removeClass('active');
            $('ul #side-menu').removeClass('active');
            $('#side-menu:nth-child(' + secNum2 + ')').addClass('active');
        } else {
            let secNum3 = secNum2 - 5;

            $('ul #side-menu').removeClass('active');
            $('ul #side-menu-child').removeClass('active');
            $('#side-menu-child:nth-child(' + secNum3 + ')').addClass('active');
        }
    }
</script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jQueryのクラスセレクタは約8~11倍、getElementByIdより遅いことがあるぞ ~ DOMを10,000回取得しただけ ~

DOM取得にどれくらい時間がかかるのか、数値として知りたかったので調べた。

  • 環境
    • MacOS Mojave(2019) Chrome最新
    • ベタ書きのHTMLファイルを用意して、JSもそこに書いちゃう。
    • jQueryは3系
    • VSCode拡張の LiveServer経由で開く。
  • やること
    • DOMを10,000回取得
  • やるやつ
    • Document.getElementById() (<= 一番速そう)
    • Document.getElementsByClassName()
    • jQuery IDセレクタ これ => (“#id”)
    • jQuery IDセレクタ これ => (“.class”) (<= 一番遅そう?) 

1. 計測結果

以下みたいな感じになりました。

処理 速度 倍率
getElementById() 1.372 ms x1
getElementsByClassName() 1.374 ms x1.002
jQuery IDセレクタ() 5.203 ms x3.792
jQuery クラスセレクタ 12.028 ms x8.767

何回かやったら、だいたい x8~11。複数回やって正確な平均欲しいけれど今日はやらない。意外にもbyClassNameの方がbyIdと同じくらいの速度で嬉しい。


以上です。
以下は余談です。

2. 計測手順

2.1. HTMLを用意

    <!-- jQueryも読んでおく -->
    <div id="hello-id" class="hello-class">hello</div>

2.2. 計測するためのコードを用意

      const DisplayProcessTime = (object) => {
        const N = 10000;
        console.time(object.name);
        for (let i = 0; i < N; i++) {
          object.func();
        }
        console.timeEnd(object.name);
      };

2.3. 計測される側の処理を用意

なんとなく配列でまとめた。

      const domGetters = [
        {
          name: "Document.getElementById()",
          func: () => document.getElementById("hello"),
        },
        {
          name: "Document.getElementsByClassName",
          func: () => document.getElementsByClassName("hello"),
        },
        {
          name: "jQuery_ID",
          func: () => $("#hello"),
        },
        {
          name: "jQuery_class",
          func: () => $(".hello"),
        },
      ];

2.4. 計測する

      for(let i = 0; i < domGetters.length; i++) {
          DisplayProcessTime(domGetters[i]);
      }

2.5. ブラウザコンソールで確認(おわり)

スクリーンショット 2021-03-04 0.33.41.png

処理 速度 順位
getElementById() 1.3720703125 ms 1
getElementsByClassName() 1.3740234375 ms 2
jQuery IDセレクタ() 5.203857421875 ms 3
jQuery クラスセレクタ 12.028076171875 ms 4
  • Chromeしか計測してない
  • 何回かやったけれど、だいたいこれくらいの感じ
  • これもさらにループして平均出したいけど、今日はやらない

3. おわりに

DOM取得にどれくらい時間がかかるのか、数値として知りたかったので調べた。
全部の環境でそうとは限らないから、本当のところはよくわからない。

書き方や使いどころを間違えないことは大事。

getElement~ って書くのがめんどい時は、ヘルパー関数的なのをつくればよいかも。

計測にかかった時間よりもこの記事を書く時間の方が多くかかった。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jQueryのクラスセレクタは約8~11倍、getElementByIdより遅いことがあるぞ (DOMを10,000回取得しただけ)

DOM取得にどれくらい時間がかかるのか、数値として知りたかったので調べた。

  • 環境
    • MacOS Mojave(2019) Chrome最新
    • ベタ書きのHTMLファイルを用意して、JSもそこに書いちゃう。
    • jQueryは3系
    • VSCode拡張の LiveServer経由で開く。
  • やること
    • DOMを10,000回取得
  • やるやつ
    • Document.getElementById() (<= 一番速そう)
    • Document.getElementsByClassName()
    • jQuery IDセレクタ これ => (“#id”)
    • jQuery IDセレクタ これ => (“.class”) (<= 一番遅そう?) 

1.1. 計測結果

以下みたいな感じになりました。

処理 速度 倍率
getElementById() 1.372 ms x1
getElementsByClassName() 1.374 ms x1.002
jQuery IDセレクタ 5.203 ms x3.792
jQuery クラスセレクタ 12.028 ms x8.767

何回かやったら、だいたい x8~11。複数回やって正確な平均欲しいけれど今日はやらない。意外にもbyClassNameの方がbyIdと同じくらいの速度で嬉しい。

1.2. 計測結果(追記: 2021.03.04)

document.querySelector()メソッドと、Document.querySelectorAll()メソッドの計測が抜けていました(コメントくださった方、ありがとうございます)。そのため、この2つを加えて再計測しました。

処理 速度 倍率 順位(1 ~ 8)
getElementById() 1.297 ms x1.000 1
getElementsByClassName() 1.292 ms x0.996 1
jQuery IDセレクタ 5.075 ms x3.913 7
jQuery クラスセレクタ 11.831 ms x9.122 8
querySelector()_(ID取得) 新規 2.250 ms x1.735 4
querySelector()_(クラス取得) 新規 1.500 ms x1.157 3
querySelectorAll()_(ID取得) 新規 3.929 ms x3.030 6
querySelectorAll()_(クラス取得) 新規 3.740 ms x2.883 5

querySelectorではIDとクラスの両方を取得できるので、両方の結果を書いています。

  • getElementById と getElementsByClassName は同じくらいの速度。今回のように、byClassNameの方が早いことも時々ある。
  • querySelector()メソッドは、クラス指定で取得した方が早い
  • querySelectorAll() においても、クラス指定の方が早い。マッチしたものを全て探すので、当然にquerySelectorより遅い。

1.3. 順番整理(追記: 2021.03.04)

実行にかかった時間の早い順に並べます。

getElementById() <=
getElementsByClassName() <
querySelector()_(クラス取得) <
querySelector()(ID取得) <
querySelectorAll()
(クラス取得) <
querySelectorAll()_(ID取得) <
jQuery IDセレクタ <
jQuery クラスセレクタ


以上です。
以下は余談です。

2. 計測手順

2.1. HTMLを用意

    <!-- jQueryも読んでおく -->
    <div id="hello-id" class="hello-class">hello</div>

2.2. 計測するためのコードを用意

      const DisplayProcessTime = (object) => {
        const N = 10000;
        console.time(object.name);
        for (let i = 0; i < N; i++) {
          object.func();
        }
        console.timeEnd(object.name);
      };

2.3. 計測される側の処理を用意

なんとなく配列でまとめた。

      const domGetters = [
        {
          name: "Document.getElementById()",
          func: () => document.getElementById("hello"),
        },
        {
          name: "Document.getElementsByClassName",
          func: () => document.getElementsByClassName("hello"),
        },
        {
          name: "jQuery_ID",
          func: () => $("#hello"),
        },
        {
          name: "jQuery_class",
          func: () => $(".hello"),
        },
      ];

2.4. 計測する

      for(let i = 0; i < domGetters.length; i++) {
          DisplayProcessTime(domGetters[i]);
      }

2.5. ブラウザコンソールで確認(おわり)

スクリーンショット 2021-03-04 0.33.41.png

処理 速度 順位
getElementById() 1.3720703125 ms 1
getElementsByClassName() 1.3740234375 ms 2
jQuery IDセレクタ() 5.203857421875 ms 3
jQuery クラスセレクタ 12.028076171875 ms 4
  • Chromeしか計測してない
  • 何回かやったけれど、だいたいこれくらいの感じ
  • これもさらにループして平均出したいけど、今日はやらない

2.6. 再計測 => ブラウザコンソールで確認(追記: 2021.03.04)

再計測を行った時の結果を残しておきます。

スクリーンショット 2021-03-04 21.11.03.png

処理 速度 順位(1 ~ 8)
getElementById() 1.297119140625 ms 1
getElementsByClassName() 1.2919921875 ms 1
jQuery IDセレクタ() 5.074951171875 ms 7
jQuery クラスセレクタ 11.8310546875 ms 8
querySelector()(ID取得) 2.249755859375 ms 4
querySelector()(クラス取得) 1.4990234375 ms 3
querySelectorAll()(ID取得) 3.92919921875 ms 6
querySelectorAll()(クラス取得) 3.74072265625 ms 5

3. おわりに

DOM取得にどれくらい時間がかかるのか、数値として知りたかったので調べた。
全部の環境でそうとは限らないから、本当のところはよくわからない。

書き方や使いどころを間違えないことは大事。

getElement~ って書くのがめんどい時は、ヘルパー関数的なのをつくればよいかも。

計測にかかった時間よりもこの記事を書く時間の方が多くかかった。

querySelectorを使用した場合の速度は、クラス指定の方がID指定より早かった点が気になる。

返り値の型の使い易さだったり、他のコード群との親和性だったりと、実際のプロダクトでは速度以外に考えるべきことは無数にある。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む