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

【カンタン】Vueで縦に自動伸縮するtextareaの作り方 | How to make auto resizing <textarea> by Vue.js

english ver

インターネッツで調べてみても中途半端なtextareaしか出てこなかったので、備忘も兼ねた記事です。

伸びるけど縮まないとか、縮むけど1文字いれるごとに2pxずつしか縮まないとか、意味なくない!?!?!?!?!?!?

んおお!?!?!?!?

まずは結果

ezgif-6-b2be62c1e8d5.gif

うおおおおおおおお
うおおおおおおおお
うおおおおおおおお
うおおおおおおおお
うおおおおおおおおうおおおおおおおお
うおおおおおおおお
うおおおおおおおおうおおおおおおおおうおおおおおおおお
うおおおおおおおお
うおおおおおおおお
うおおおおおおおお

コード

今気づいたけどマークダウンの候補にvuejsってある。。
便利すぎない?

業務で書いたコードからいらないやつ消した感じなので、このまま動かなかったらごめんなさい。
編集リクエスト送っていただければーーー

MyTextarea.vue
<template>
  <textarea
    class="textarea bg-white"
    :style="textareaStyle"
    :placeholder="placeholder"
    :value="value"
    @input="handleInput($event)"
  />
</template>

<script lang="ts">
import Vue from "vue"

export default Vue.extend({
  props: {
    placeholder: { type: String, default: "" },
    value: { type: String, default: '' }
  },
  data() {
    return {
      textareaHeight: 100 // デフォルト値いれとく。minHeightといっしょでよい。borderあるのでちょっとずれる
    }
  },
  computed: {
    textareaStyle(): object { // 動的にtextareaのheightをいじれるようにしている
      return {
        height: `${this.textareaHeight}px`
      }
    }
  },
  methods: {
    async handleInput(event: any) { // 入力されるたびによばれる。anyなのはゆるして。。。
      this.$emit('input', event.target.value) // これは親に伝えるためだけ。

      this.textareaHeight = 0 // ミソ。一旦textareaのheightを0にしちゃう

      await this.$nextTick() // さらにミソ。ここで待たないとDOMの更新前に下のコードが走って変な挙動になる

      // heightが0になった瞬間textareaはminHeight: 100になる
      // 入力済み文字列の高さが100を超えたら、scrollHeightが必要な分だけ大きくなる = それをheightにしちゃえばOK!
      this.textareaHeight = event.target.scrollHeight
    }
  }
})
</script>

<style lang="stylus" scoped>
.textarea-container {
  width: 100%;
}

.textarea {
  width: 100%;
  min-height: 100px; // ここはお好み。変えるならdata()の値も変えるとよいよ
  border: 1px solid #D9D9D9
  border-radius: 4px;
  padding: 5px 12px;

  &::placeholder {
    color: #D9D9D9
  }
}
</style>

TL;DR

this.$nextTickがミソなんじゃぞ。

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

4歳娘「パパ、20歳以上のユーザーを抽出して?」

ある日、ピクニックで訪れた山にて

娘(4歳)「パパ、見て!」

ワイ「なんや、娘ちゃん」

娘「あそこに、家族風のオブジェクトが落ちてるよ!」

JavaScript
const family = {
    mother: {
        name: "よめ太郎",
        age: 35
    },
    father: {
        name: "やめ太郎",
        age: 37
    },
    daughter: {
        name: "娘ちゃん",
        age: 4
    }
};

ワイ「おお、ほんまや!」
ワイ「mother、father、daughterの3人家族やな!」

娘「せっかくだから、この中から20歳以上のユーザーを抽出してみようよ!」

ワイ「おお!せっかくやからな!」
ワイ「やってみよか!」
ワイ「でも、家族のことをユーザーって呼ぶのはやめてな!」
ワイ「そんで、ええと」
ワイ「大人だけを抜き出したオブジェクトを作ればええんやったな!」
ワイ「ほな、やってみるで!」

JavaScript
const otona = {};

Object.keys(family).forEach(key => {
    const person = family[key];

    if (person.age >= 20) {
        otona[key] = person;
    }
});

娘「わぁ、すごい!」
娘「パパ、このコードがどういう意味だか説明して?」

ワイ「おお、ええで!」
ワイ「まず」

JavaScript
const otona = {};

ワイ「otonaって名前の空オブジェクトを作るんや」
ワイ「そこに、familyの中から20歳以上の人間だけを詰め直していくんや」

娘「うんうん」

ワイ「そのためにまず」
ワイ「Object.keys(family)を使って」
ワイ「familyというオブジェクトの、key名たちを配列として取得するんや」

娘「オブジェクトの、key名たちを取得・・・」
娘「ってことはつまり、Object.keys(family)は」
娘「["mother", "father", "daughter"]と同じってことだね!」

ワイ「その通りや!」
ワイ「せやから、↓こんなイメージや」

JavaScript
["mother", "father", "daughter"].forEach(key => {
    /* 省略 */
});

ワイ「↑こんな感じで、配列のforEachメソッドを使って」
ワイ「motherfatherdaughterと順番に回しながら」
ワイ「if文で20歳以上かどうか調べて」
ワイ「20歳以上の人間だけをotonaオブジェクトに詰め詰めしていくんや」

JavaScript
["mother", "father", "daughter"].forEach(key => {
    // keyには "mother" か "father" か "daughter" が入ってくる。
    const person = family[key];

    if (person.age >= 20) {
        otona[key] = person;
    }
});

ワイ「↑こんな感じやな」

娘「なるほど〜」

ハスケル子「イマイチですね」

ワイ「!?

ハスケル子も来ていた

ワイ「おお、ハスケル子ちゃん」
ワイ「ハスケル子ちゃんもこの山に遊びに来とったんか」

ハスケル子「はい、私もこの山に関数拾いに来ていました」

ワイ「おお、ほんまに好きやな〜」
ワイ「ところで、さっきのコードの何がイマイチなん?」

ハスケル子「うまくは言えませんが、私ならこう書きます」

JavaScript
const otona =
    Object.fromEntries(
        Object.entries(family)
            .filter(([, person]) => person.age >= 20)
    );

ワイ「Object.fromEntries()Object.entries()・・・」
ワイ「何なん、このメソッドたち・・・?」

ハスケル子「Object.entries()は、オブジェクトから配列を作ってくれるやつです」

ワイ「オブジェクトから配列を作る・・・?」

ハスケル子「はい」
ハスケル子「例えば、Object.entries(family)は・・・」

JavaScript
[
    ["mother", { name: "よめ太郎", age: 35 }],
    ["father", { name: "やめ太郎", age: 37 }],
    ["daughter", { name: "娘ちゃん", age: 4 }],
]

ハスケル子「↑これと同じ意味になります」

ワイ「へぇ〜、オブジェクトからこんな感じの配列を生成してくれるんや・・・」
ワイ「じゃあ、もう一つのObject.fromEntries()はどんなメソッドなん?」

ハスケル子「Object.fromEntries()は、さっきのObject.entries()の逆ですね」
ハスケル子「さっきObject.entries()で作った配列をObject.fromEntries()に渡してあげると」
ハスケル子「元のオブジェクトに戻ります」
ハスケル子「つまり・・・」

JavaScript
const obj = Object.fromEntries(
        Object.entries(family)
    );

ハスケル子「↑こうすると、変数objにはfamilyそのままのオブジェクトが入るってことです」

ワイ「ふーん」
ワイ「オブジェクトから一回配列を作って、その配列からまたオブジェクトを作る」
ワイ「そんなことして何が嬉しいん?」

ハスケル子「一度配列にすることによって」
ハスケル子「配列の持っているfilter()やらmap()やらのメソッドを使って」
ハスケル子「好きなように整形できるのが嬉しいですね」
ハスケル子「今回であれば、20歳以上の人間だけを抽出したい訳ですから」
ハスケル子「filter()メソッドが使えるじゃないですか」

JavaScript
Object.entries(family)
    .filter(([, person]) => person.age >= 20)

ハスケル子「↑こうしてフィルターしてやるんです」
ハスケル子「そうすると・・・」

JavaScript
[
    ["mother", { name: "よめ太郎", age: 35 }],
    ["father", { name: "やめ太郎", age: 37 }],
]

ハスケル子「↑こんな配列が出来上がります」
ハスケル子「この配列をObject.fromEntries()でオブジェクトに戻してあげると」

JavaScript
{
    mother: {
        name: "よめ太郎",
        age: 35
    },
    father: {
        name: "やめ太郎",
        age: 37
    }
};

ハスケル子「↑このように、20歳以上の人間だけが入ったオブジェクトになってる」
ハスケル子「っていう訳ですね」

ワイ「おお〜」

まとめ

  • Object.entries()を使うとオブジェクトから配列を生成できる。
  • Object.fromEntries()を使うと配列を元にオブジェクトを生成できる。
  • オブジェクトから一度配列に変換することで、配列の持っている便利なメソッドたちを使える。
  • オブジェクトを色々と整形したい時に便利。

ワイ「・・・↑こういうことやな!ハスケル子ちゃん」

ハスケル子「そんな感じです」

ワイ「おおきにやで!」
ワイ「また一つ賢くなったわ!」

〜おしまい〜

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

久しぶりに create-react-app を使ったら動かなかった話

事象

最近はいつも React Native を書いてるので、たまには React でも触ろうとして、公式に従いnpx create-react-app コマンドを実行したら、

node_modules/
package.json
yarn.lock

しか存在しないプロジェクトが生成されました。
しかも、 package.json は、

{
  "name": "PROJECT_NAME",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-scripts": "3.4.1",
  },
}

というシンプル極まりないもの。
グローバルインストールした create-react-app を削除しても直りません。

解決策

GitHub の issue にあるように、再度グローバルインストールをして、 npx create-react-app を実行すると直りました。

久しぶりに触ると色々とトラブルに直面しますね。不幸だ(ぇ

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

Vue.js でインクリメンタルサーチ

インクリメンタルサーチとは

インクリメンタルサーチ(英語: incremental search)は、アプリケーションにおける検索方法のひとつ。検索したい単語をすべて入力した上で検索するのではなく、入力のたびごとに即座に候補を表示させる

完成品

スクリーンショット 2020-04-02 22.40.28.png

スクリーンショット 2020-04-02 22.40.38.png

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>インクリメンタルサーチ</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div id="app">
<!-- v-model は任意の form 要素にある value、checked、
または selected 属性の初期値を無視します。input または 
textarea は常に、信頼できる情報源として Vue インスタンスを扱います。
コンポーネントの data オプションの中で JavaScript 側で初期値を宣言する必要があります -->
        <input type="text" placeholder="検索" v-model="search">

        <select v-model="sort">
            <option value="">ソート無し</option>
            <option value="asc">昇順</option>
            <option value="desc">降順</option>
        </select>

        <transition-group tag="ul">
            <li v-for="item in sortedList" v-bind:key="item.id">
                {{ item.text }}
            </li>
        </transition-group>
    </div>

    <script src="main.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</body>
</html>

JS

new Vue({
    el: "#app",
    data:{
        search: '',
        sort: '',
        list: [
            { id: 1, text: 'Python' },
            { id:2, text: 'Ruby'},
            { id:3, text: 'PHP'},
            { id:4, text: 'JavaScript'},
            { id:5, text: 'Java'},
            { id:6, text: 'Go'},
            { id:7, text: 'C'},
            { id:8, text: 'C#'},
            { id: 9, text: 'Rails' },
            { id:10, text: 'Django'},
            { id:11, text: 'MySQL'},
            { id:12, text: 'Vue.jst'},
            { id:13, text: 'react'},
            { id:14, text: 'Docker'},
            { id:15, text: 'unity'},
            { id:16, text: 'jQuery'}
        ]
    },
    computed: {
        filteredList: function(){
            return this.list.filter(function(item){
                return item.text.indexOf(this.search) > -1
            }, this)
        },

        sortedList: function(){
            var copy = this.filteredList.slice()

            if(this.sort === 'asc' ){
                return copy.sort(this.comparatorAsc)
            } else if(this.sort === 'desc') {
                return copy.sort(this.comparatorDesc)
            } else{
                return copy
            }
        }
    },
    methods:{
        comparatorAsc: function(itemA, itemB){
            if(itemA.text < itemB.text){
                return -1
            } else if(itemA > itemB.text){
                return 1
            } else{
                return 0
            }
        },

        comparatorDesc: function(){
            if(itemA.text > itemB.text){
                return -1
            } else if(itemA < itemB.text){
                return 1
            } else{
                return 0
            }
        },
    }

});

CSS

body{
    font-family: sans-serif;
}

input,
select{
    padding: 2px 8px;
    font-size: inherit;
    vertical-align: middle;
}

ul {
    position: relative;
    margin-top: 6px;
    padding: 0;
    width: 300px;
    list-style: none;
}

li{
    margin: 0;
    padding: 10px;
    border-bottom: 1px solid #ddd;
}

.v-move{
    transition: transform 300ms ease-out;
}

.v-enter-active {
    transition: 300ms;
}

.v-enter{
    opacity: 0;
}

.v-enter-to{
    opacity: 1;
}

.v-leave-active{
    transition: 300ms;
}

.v-leave{
    opacity: 1;
}

.v-leave-to{
    opacity: 0;
}

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

Kinx ライブラリ - Signal

Signal

はじめに

「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。言語はライブラリが命。ということでライブラリの使い方編。

今回は Signal です。

簡単ながらシグナルをサポートしました。

Signal

Signal は、type として Signal.SIGINTSignal.SIGTERM のみサポート。Signal.trap(type, handler) でシグナル・ハンドラを登録し、処理を実行する。ハンドラが登録されていない状態でシグナルを検知したら、プログラムが終了する。

Signal.trap(type, handler)

シグナル・ハンドラ(handler)を登録する。id を返すので、登録解除したい場合は Signal.remove(id) で登録したハンドラを解除できる。

ハンドラが明示的に false を返した(return false した)場合、プログラムを終了する。

現時点ではハンドラは登録した順序でコールされるが、この順序を保証するかは決めていない。保証する必要があるかな?

Signal.remove(id)

id で示されたハンドラを解除する。

ブロッキング処理の中断

以下の組み込みメソッドでのウェイト中には検出して中断するようにしている。

  • System.sleep()
  • $stdin.getch()
  • $stdin.readLine()

サンプル

Ctrl-C では終了しないが、Ctrl-Break で終了する。ただし、Ctrl-C を 10 回受信したら、ループから抜けてプログラムを終了させる。

var trapped = 0;

Signal.trap(Signal.SIGINT, function() {
    System.println("SIGINT");
    ++trapped;
});

Signal.trap(Signal.SIGTERM, function() {
    System.println("SIGTERM");
    return false;
});

while (trapped < 10) {
    System.println("Trapped = %2d" % trapped);
    System.sleep(1000);
}

System.println("Ended");

結果。

Trapped =  0
Trapped =  0
Trapped =  0
SIGINT
Trapped =  1
SIGINT
Trapped =  2
SIGINT
Trapped =  3
SIGINT
Trapped =  4
SIGINT
Trapped =  5
SIGINT
Trapped =  6
SIGINT
Trapped =  7
SIGINT
Trapped =  8
SIGINT
Trapped =  9
SIGINT
Ended

Windows で Ctrl-Break、または Linux で kill -15 [PID] してみる。

Trapped =  0
Trapped =  0
Trapped =  0
SIGINT
Trapped =  1
SIGINT
Trapped =  2
SIGINT
Trapped =  3
Trapped =  3
SIGTERM

おわりに

今回は簡単に説明。シグナルは割り込みと同じで急にフローが変化するので、処理前後でつじつま合うようにするために結構手が入りました。内部仕様はもう少し変化するかもしれない。動作仕様は変わらない範囲で。

あと、Windows と Linux で色々仕様が違うのを何とかするのが大変。Linux だと fgets とかシグナルで中断されないので siginterrupt() とか使ってシステムコールから復帰させたりしないといけない。ハンドルされるタイミングも微妙に違う。

SIGINTSIGTERM 以外サポートする必要あるかな?

ではまた次回。

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

Haskellのプログラムは '現実世界'を引数にとり、プログラムが影響を与えた'現実世界' を返す純粋関数

はじめに

純粋関数型プログラミング言語は純粋なのにどうやって入出力を行うのか概念的にわからない。という質問をよくいただくので、解説をして行こうかなと思います。

タイトルで出落ちです。

対象読者はHaskellを学びたてだったり、ML型の言語をちょっと触ったことがある程度の人ですが、
解説の為にJavaScriptを多用しますので、JavaScriptが読めれば楽しめるかと思います。

これを読んでコードをかけるようにはならないとは思いますが、概念的に面白いし実用性あるじゃん〜と思ってもらえればいいなと思います。

概念的な説明の簡単のために、正確ではない表現が登場しますが、
概念的な説明の簡単のためになってなく、正確ではない表現があった場合は優しく指摘していただけると幸いです。

純粋関数型プログラミングとは

純粋関数型プログラミング言語と呼ばれる言語が存在します。純粋関数型プログラミング言語は記述する関数を副作用のない純粋な数学的関数として定義し、プログラムを構築します。

純粋関数などの例

足し算をする純粋関数

JavaScript
function add(n, m) {
  return n + m;
}

足算をして表示をする関数 こちらは純粋ではない関数

JavaScript
function add(n, m) {
  console.log(n + m);
}

コンソールから数値を受けて足算をする関数 こちらも純粋関数ではない

JavaScript
function add() {
  const n = console.read();
  const m = console.read();
  return n + m;
}

console.read()は冗長になってしまうのでイメージ

純粋関数型言語では、副作用与えうる関数や入力によって出力が決定しない関数は関数と呼びません。
上記の例では計算をした上でconsoleに表示したり、consoleからreadして値を返す関数などです。

HaskellのHello world!

Haskellではコンソールで文字を読み込んで出力するプログラムはこのように記述します。

Haskell
main :: IO ()
main = do
  x <- getLine
  print x

関数なのにめっちゃ入出力行ってんじゃん!!全然純粋じゃなく見える!!!

エントリーポイントであるmain関数はIO ()というよくわからない型になってますね。
最初はC言語のint main() return 0;ぐらいに思っておけばいいですが、なかなか面白い概念を包み込んでいます。この概念を解説していきます。

状態遷移

IO ()型を解説する前に、Haskellにおける状態遷移を関数で表す方法を知る必要があります。

純粋関数型プログラミング言語では、入出力だけでなく変数の値を変更することもできません。
純粋関数しかないため、純粋関数から関数外の変数の値を変更したらそれはもう純粋関数ではないからです。

しかしながら純粋関数型言語でもこの状態遷移を関数で表すことができます。

まずは一般的なJavaScriptのプログラム

何度カウンターを使ったかを返すプログラム 純粋関数ではない

JavaScript
let state = 0;

function count() {
  state = state + 1;
}

function stateTransition() {
  count();
  count();
  count();
  count();
}

// 結果
const result = state;

純粋関数のみで状態遷移を表現するプログラム

JavaScript
function count(state) {
  return state + 1;
}

function stateTransition(state) {
  return count(count(count(count(state))));
}

// 結果
const result = stateTransition(0);

純粋関数型プログラミングでは、状態を関数の引数と返り値として持ち回すことで状態を表現できます。
しかし、状態を持ち回すのは状態を必要とする関数全てに新たに引数と返り値を追加せねばいけなく非常に冗長になってしまいます。

そこでHaskellではStateモナドと呼ばれる関数の型?DSL?が存在し、状態遷移を簡潔に純粋関数のまま扱うことができます。

code-state2を実現するHaskellのプログラム

Haskell
-- 状態を遷移させる関数
count :: State Int ()
count = modify (+ 1)

-- 状態を遷移させる関数を逐次実行
stateTransition :: State Int ()
stateTransition = do
  count
  count
  count
  count

-- 状態を遷移させる関数の引数に0を入れて遷移させた後の結果を取得
result :: Int
result = execState stateTransition 0

Stateを引き継いで渡す作業を、Stateモナドの文脈の中では順番に書くだけでよくなります。

JSでやってたこの処理を

JavaScript
function stateTransition(state) {
  return count(count(count(count(state))));
}

こう書ける!!

Haskell
stateTransition :: State Int ()
stateTransition = do
  count
  count
  count
  count

Haskellでは関数呼び出しに()などは必要ないことなどを考慮すると、
副作用のあるJavaScriptの例にかなり似ていることがわかります。

JavaScript
function stateTransition() {
  count();
  count();
  count();
  count();
}

状態を利用したプログラム

ただこのままだと状態を遷移させているだけで状態を利用して何かをしたいということが何もできないですね。
状態を利用しつつ、状態を遷移させていくプログラムをサンプルで書きます。

扱いたい処理は先ほどの状態をカウントして値を増やしつつ、返り値として数字を文字列にしたモノにしましょう。
非純粋なJavaScriptで書いてこんな感じ。

JavaScript
let state = 0;

function count() {
  state = state + 1;
  return state.toString();
}

function stateTransition() {
  const one   = count();
  const two   = count();
  const three = count();
  const four  = count();
  return one + two + three + four;
  // "1234"
}

これを純粋関数のみでJavaScriptで表します。

JavaScript
function count(state) {
  return [state + 1, state.toString()];
}

function stateTransition(state) {
  const [state1, one]   = count(state);
  const [state2, two]   = count(state1);
  const [state3, three] = count(state2);
  const [state4, four]  = count(state3);
  return one + two + three + four; 
}

// 結果
const result = stateTransition(0);

このように状態を引数にとって、状態と欲しい値をセットで返す->そして次の関数には状態を渡す を繰り返していけば状態を利用したり状態を変更したりしながら、純粋関数のみで計算を行うことができます。

Haskellでは先述のように、Stateモナドを利用して状態を意識せずに次から次へと移していくことが受け継いでいくことができます。

State Int Stringという型はIntは状態の型で、引数と返り値に存在し、Stringは返り値にのみ存在するという型の別名です。
Int -> (Int String)これの別名みたいなもんです。

Haskell
count :: State Int String
count = do
  modify (+ 1)
  state <- get
  return (show state)

stateTransition :: State Int String
stateTransition = do
  one   <- count
  two   <- count
  three <- count
  four  <- count
  return (one ++ two ++ three ++ four)

result :: String
result = evalState stateTransition 0

こちらも非純粋関数で書いたJSの状態遷移の計算とかなり似てますね。

JavaScriptでいう、この純粋関数で表している状態が絡んだ計算を、

JavaScript
function stateTransition(state) {
  const [state1, one]   = count(state);
  const [state2, two]   = count(state1);
  const [state3, three] = count(state2);
  const [state4, four]  = count(state3);
  return one + two + three + four; 
}

Haskellでは純粋関数のまま簡潔に書ける!

Haskell
stateTransition :: State Int String
stateTransition = do
  one   <- count
  two   <- count
  three <- count
  four  <- count
  return (one ++ two ++ three ++ four)

非純粋に書いた時とすごくよく似てる!!

JavaScript
function stateTransition() {
  const one   = count();
  const two   = count();
  const three = count();
  const four  = count();
  return one + two + three + four;
}

さて、そろそろ純粋関数のみで状態が絡んだ計算を記述するイメージは掴めてきたのではないでしょうか。

状態が絡む計算まとめ

状態を引数として関数でもらって、状態と状態を利用した結果をセットで返り値として返すだけで状態が絡んだ計算ができる。

Haskellでは逐次実行しているように見える関数も、実は純粋関数を関数合成しただけである。

入出力

状態遷移の続きとして、ちょっとした遊びをします。

状態としての配列と、値として文字列があります。

putという関数を定義します。
状態としての配列と文字列を受け取り、
配列に文字列を追加して、
状態としての配列と '何もない' を返す型です。

JavaScript
function put(state, string) {
  state.push(string);
  return [state, null];
}

次にgetという関数を定義します。
状態としての配列を引数として受け取り、
配列の最後の文字列と、状態としての配列を返します。

JavaScript
function get(state, string) {
  const end = state[state.length - 1];
  return [state, end];
}

これを使って遊んでみましょう。

JavaScript
function main(state1) {
  const [state2, str] = get(state1);
  const [state3, _]   = put(state2, str);
  return state3;
}

main(["hello"]); 

最後の状態であるstate3は、 ["hello","hello"] になりますね。

Haskellで全く同じ動作をするputとgetです。

非純粋なJavaScriptの例は書きませんが、先述2例のように似ているはずです。

Haskell
get' :: State [String] String
get' = do
  state <- get
  let str = last state
  return str

put' :: String -> State [String] ()
put' str = modify (++ [str])

遊んでみましょう。

Haskell
main' :: State [String] ()
main' = do
  str <- get'
  put' str

result = execState main' ["hello"]

最初の状態として、引数["hello"]を与えてあげると、["hello", "hello"]という最終状態になります。

何かに似てきました。チラっ

haskellの標準入力を受け取って、標準出力をそのまま返すプログラムです

Haskell
main :: IO ()
main = do
  str <- getLine
  print str

このプログラムのコンソールの結果をみてみましょう

$ ./echo
hello
hello

先ほどのputとget関数で遊んだ入力や結果

最初の状態として、引数["hello"]を与えてあげると、["hello", "hello"]が帰ってきます。

echo-exeを実行したときのコンソールの画面がどことなく似ていませんか?

IO型は、

hello

と書かれたコンソールが存在する世界を状態として引数にとって、

hello
hello

と書かれたコンソールが存在する世界を返す純粋関数の型だったんだ!!!!

IO型は '現実世界'を引数にとり、プログラムが影響を与えた'現実世界' を返す純粋関数

State T Uの状態Tを現実世界に特殊化した型をIO U型、
状態ではない結果の型Uを何もないことを表す()に特殊化すると
IO ()型となります。
Haskellのmain関数の型ですね。

そうやってみてみると、main関数の型は

Haskell
-- main :: State RealWorld a

main :: RealWorld -> (RealWorld, ())

こんな感じになって、確かに現実世界を引数にとって、現実世界を戻り値に持っているんだなとわかります。

何言ってんだこいつと思われるかもしれませんが、実装も本当にそのテイになっています。

Haskell
newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))

このIO ()であるmain関数にHaskellランタイムは現実世界を注入して、現実世界を書き換えてくれる純粋関数だったんですね。

まとめ

純粋関数型プログラミング言語であるHaskellは、解説の他に因果的決定論を持ち込む、IOを剥がすunsafeな処理を行わない、などの条件を色々付け加えれば入出力も純粋関数として見ることができるのではないでしょうか?

余談

モナドって難しそうだけどStateモナドの場合は中身がただの関数だヨ
純粋関数型の言語でもこうやって普通の動的なスクリプト言語であるJavaScriptのように簡潔にコードが書けるんだネ
(状態を取り扱っていたりIOを扱っていたりが型で制限できるので更に利点も多い)

StateとかIOだけじゃなくて非同期計算やNothingのChainingなどほとんど全ての計算構造を簡単に書けるようにしてくれるので興味を持ったらHaskellやってみてね✨

(強いHaskellerの皆さん、心理的安全性の高いツッコミ待ってます(心が弱いので))

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

javascriptで入力キーを取得(メモ)

1.まずはコンソールに出力

// キーが離れたときにコンソールに出力
window.addEventListener("keyup",(e) =>{
    console.log(e.key);  
});

キーが離れたとき(keyup)コンソールに出力します。

2.文字を画面に出力

<body>
    <!--ここに文字を出力させる-->
    <div id="output"></div>

    <script>
        // 要素取得
        const output = document.querySelector("#output");

        window.addEventListener("keyup",(e) =>{ 
            // keyをHTMLで出力       
            output.innerHTML = e.key;
        });
    </script>    
</body>

入力したキーが一文字づつ出力されます。

3.入力した文字列を画面出力(配列)

<body>
    <div id="output"></div>

    <script>
        const array =[]; //文字格納用の配列
        const output = document.querySelector("#output");
        window.addEventListener("keyup",(e) =>{
            array.push(e.key); //配列の後ろに追加
            output.innerHTML = array;
        });
    </script>    
</body>

配列にプッシュpush()します。文字がどんどん追加されます。

4.入力した文字列を画面出力

<body>
    <div id="output"></div>

    <script>
        const array =[];
        const output = document.querySelector("#output");
        window.addEventListener("keyup",(e) =>{
            array.push(e.key);
            const str = array.join(""); //配列を文字列に変換
            output.innerHTML = str;
        });
    </script>    
</body>

上の例だとa,r,r,a,yみたいに出力されるので、配列の結合にjoin()を使用します。

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

vueの勉強し直し

Javascriptのフレームワーク「vue.js」について書いていきます。

まずは Hello World

<div id="app">{{ message }}</div>
const app = new Vue({
    el: '#app',
    data: {
        message: 'Hello World'
    }
})

変数を

{{}}

で囲むことでHTMLとして表示することができます。

双方向データバインディング

vueと言ったらこれが最初に思い浮かびました。

初めて実装したときは感動しました。

<div id="app">
    <p>{{ inputText }}</p>
    <input v-model="inputText">
</div>
const app = new Vue ({
    el: '#app',
    data: {
        inputText: ''
    }
})

テキストフィールドで入力した文字がリアルタイムでテキストとして反映されます。

メソッドも追加できます

<div id="app">
    <p>{{ count }} 回クリックされました!</p>
    <button v-on:click="counter">押してね</button>
</div>
const app = new Vue ({
    el: '#app',
    data: {
        count: 0
    },
    methods: {
        counter: function(){
            this.count++
        }
    }
})

vanillaや、jQueryのようにclickなどのイベントのメソッドも設定することがでいます。

<button @click="counter">押してね</button>

と書き換えることができます。

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

プリコネR 防御力計算機 (Vue.js + Firebase)

概要

  • Vue.jsとFirebaseを使って簡単なサービスを作ってみました。
  • プリコネRの攻撃力と与ダメージから防御力を計算するツール。
  • リンク:https://r-tools-69dd3.web.app/ R-tools_x4.gif

ソースコード

index.html
<div class="calc">
    <p>攻撃力:<input type="text" v-model="attack"></p>
    <p>ダメージ:<input type="text" v-model="damage"></p>
    <p>防御力:<input type="text" v-model="defense"></p>
</div>
js/main.js
(function(){
  'use strict';

  let vm = new Vue({
     el: '#app',
     data: {
       attack: 5000,
       damage: 5000,
     },
     computed: {
      defense: {
        get: function () {
          return Math.round(100 * (this.attack / this.damage - 1))
        },
        set: function (defense) {
          this.damage = Math.round(this.attack / (1 + defense / 100))
        }
      },
    }
  })

}())

アプリの公開

Firebase(無料枠)で公開しています。こちらの記事を参考にさせていただきました。
Vue.js + FirebaseでTodoアプリを作る

参考文献

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

プリコネR 防御力計算機 (Vue.js+Firebase)

概要

  • Vue.jsとFirebaseを使って簡単なサービスを作ってみました。
  • プリコネRの攻撃力と与ダメージから防御力を計算するツール。
  • アプリ:https://r-tools-69dd3.web.app R-tools_x4.gif

ソースコード

index.html
<div class="calc">
    <p>攻撃力:<input type="text" v-model="attack"></p>
    <p>ダメージ:<input type="text" v-model="damage"></p>
    <p>防御力:<input type="text" v-model="defense"></p>
</div>
js/main.js
(function(){
  'use strict';

  let vm = new Vue({
     el: '#app',
     data: {
       attack: 5000,
       damage: 5000,
     },
     computed: {
      defense: {
        get: function () {
          return Math.round(100 * (this.attack / this.damage - 1))
        },
        set: function (defense) {
          this.damage = Math.round(this.attack / (1 + defense / 100))
        }
      },
    }
  })

}())

アプリの公開

Firebase(無料枠)で公開しています。こちらの記事を参考にさせていただきました。
Vue.js + FirebaseでTodoアプリを作る

参考文献

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

Vue.js+Firebaseで簡単なWebサービスを作って公開する

概要

  • Vue.jsとFirebaseを使って簡単なサービスを作ってみました。
  • プリコネRの攻撃力と与ダメージから防御力を計算するツール。
  • アプリ:https://r-tools-69dd3.web.app R-tools_x4.gif

ソースコード

index.html
<div class="calc">
    <p>攻撃力:<input type="text" v-model="attack"></p>
    <p>ダメージ:<input type="text" v-model="damage"></p>
    <p>防御力:<input type="text" v-model="defense"></p>
</div>
js/main.js
(function(){
  'use strict';

  let vm = new Vue({
     el: '#app',
     data: {
       attack: 5000,
       damage: 5000,
     },
     computed: {
      defense: {
        get: function () {
          return Math.round(100 * (this.attack / this.damage - 1))
        },
        set: function (defense) {
          this.damage = Math.round(this.attack / (1 + defense / 100))
        }
      },
    }
  })

}())

アプリの公開

Firebase(無料枠)で公開しています。こちらの記事を参考にさせていただきました。
Vue.js + FirebaseでTodoアプリを作る

参考文献

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

ハンバーガーメニューの作成

スクリーンショット 2020-04-02 18.30.51.png
スクリーンショット 2020-04-02 18.30.58.png

HTML5

<body>
    <!-- ハンバーガーボタン -->
    <div class="ham" id="ham">
    <!-- ハンバーガーボタンのライン -->
        <span class="ham_line ham_line1"></span>
        <span class="ham_line ham_line2"></span>
        <span class="ham_line ham_line3"></span>
    </div>
    <!-- メニューの中身 -->
    <div class="menu_wrapper" id="menu_wrapper">
        <div class="menu">
            <ul>
                <li><a href="#1" style="color: #FFF;">メニュー1</a></li>
                <li><a href="#2" style="color: #FFF;">メニュー2</a></li>
                <li><a href="#3" style="color: #FFF;">メニュー3</a></li>
            </ul>
        </div>
    </div>

CSS

.ham{
    position: relative;
    width: 40px;
    height: 40px;
    cursor: pointer;
    background-color: rgba(0, 0, 255, 0.911);
}
/* ハンバーガボタンのライン */
.ham_line {
    transition: all 0.3s;
    position: absolute;
    left: 10px;
    width: 20px;
    height: 2px;
    background-color: #FFF;
}
.ham_line1 {
    top: 10px;
}
.ham_line2 {
    top: 18px;
}
.ham_line3 {
    top: 26px;
}

/* Javascriptでクリックイベントが発火した後の処理 ハンバーガボタンのライン */
.clicked .ham_line1{
    transform: rotate(45deg);
    top: 20px;
}

.clicked .ham_line2{
    width: 0px;
}

.clicked .ham_line3{
    transform: rotate(-45deg);
    top: 20px;
}
/* メニューの表示 */
.menu {
    position: fixed;
    left: -400px;
    width: 300px;
    height: 300px;
    background-color: rgba(0, 0, 255, 0.849);
    transition: all 0.5s;
    color: #FFF;
}

.clicked .menu {
    left: 8px;
}

JavaScript

  const ham = document.querySelector('#ham');
  const menu_wrapper = document.querySelector('#menu_wrapper')

// addEventListener さまざまなイベント処理を実行することができるメソッド
  ham.addEventListener('click', function(){ 
// classListとは、対象要素に設定しているクラスを配列のように扱えるオブジェクト
    ham.classList.toggle('clicked');
    menu_wrapper.classList.toggle('clicked')
  });

addEventListener

https://www.sejuku.net/blog/57625

classListとは

https://techacademy.jp/magazine/22308

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

JavaScript : 一定範囲のスクロールを検知するしてクラスを付与・除去する

  • 指定した要素がブラウザ表示範囲の頂上に来たらクラスを付与する
  • 指定した要素がブラウザの表示範囲からでたらクラスを除去する

という仕様でビジュアルコントロールをするスクリプトです。
言葉だけでは分かりづらいので以下サンプル画面をスクロールしてみてください。

See the Pen range activate script by Kazuyoshi Goto (@KazuyoshiGoto) on CodePen.

「Dummy Text Section 1」のブロックが表示範囲最上部に来たらナビゲーションが出てきます。
「Dummy Text Section 1」が画面外に消えるまでスクロールするとナビゲーションが引っ込みます。

ナビゲーションのレイヤーを「show」というクラスの付与・除去でコントロールしています。
一定範囲だけである要素を表示させる」という、よくある感じのUIです。

こういう細かいネタって案外明文化されていないのでメモしておこうと思います。

JavaScript

window.onscroll = function () {
  scrollToggleClass(".section1", ".fixed-nav", "show");
}
function scrollToggleClass(rangeTarget, addTarget, classname) {
  if($(rangeTarget).length){
    scroll = $(window).scrollTop();
    startPos = $(rangeTarget).offset().top;
    endPos = startPos + $(rangeTarget).outerHeight();
    if (scroll > startPos && scroll < endPos) {
        $(addTarget).addClass(classname);
    } else {
        $(addTarget).removeClass(classname)
    }
  }
}

スクロールを検知したら scrollToggleClass() を動かす処理です。

scrollToggleClass() には範囲対象となる rangeTarget、クラスの取り外しをする addTarget、付与するクラス名 classname を指定します。

rangeTargetの開始位置と終了位置を取得し、あとはウィンドウのスクロール位置に応じてクラスを付けたり外したりしています。

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

moment.jsでミリ秒を取得する方法

moment.js 便利だお。

// 現在日時の場合
const millisecond = moment().valueOf()
// 日付を指定する場合
const millisecond = moment(new Date('2020-04-02')).valueOf()

ちなみに、momentオブジェクトが正しい日付形式になっていないと.valueOf()で取得した値はNaNになります(ハマりました)。

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

初心者がオンライン競プロに出てみた

何故

最近、自分のJavaScriptのレベルはどれくらいなんだろうと思いpaizaでスキルチェックを何度か受けていたのだが、ある記事で「AtCoder」なるサイトが紹介されていて気になったので何も準備せずに参加してみた。

標準入出力がムズイ

何も準備しないとはいっても参加登録をしなければ始まらないので一応サインアップと今日(3/28)のコンテストの参加登録をした。開始時間の21時になると、サイト上に「コンテストが始まりました」というメッセージと共に6問の問題が表示され、制限時間のカウントダウンが始まる。始まる前は「paizaと大して変わらないだろう」とたかを括っていたのだが、いざ始まるとpaizaと違って標準入出力を自力でやらなければならないので何から始めて良いか分からず、結局標準入出力のミスのせいでランタイムエラーを10回以上繰り返し、便意による妨害もあって制限時間1時間40分中半分以上を無駄にするという大失態を犯してしまった。
これからAtCoderに挑戦しようと思っている方に伝えたい。AtCoderのサンプルコードを熟読するなりコピペするなりして、始まる前に標準入出力のコーディングだけは身に付けた方が良い。

問題の難易度は高くないが...

標準入出力を乗り切り問題に取り掛かってしばらくすると、あまり問題自体は難しくないことに気がついた。情けない時間切れで3問しか解けず4問目も最後にチラッと見ただけなのだが、少なくとも3問目までは初心者の僕でもホワイトボードを使うことなく脳内で解けた。僕の現在のpaizaランクがBなので、それくらいのレベルの人であれば4問目まで解くのはそう難しくないだろう(標準入出力を予習しておけば)。
しかし、一つだけ注意しておかなければならない点がある。時たま、paizaで通ってAtCoderで通らないコードがあるということだ。例えば、

n.sort(
 function(x,y){
   return (x < y ? -1 : 1);//降順の場合は-1と1を逆にする
 }
);

上のコードは配列nを昇順に並べ替える際のJavaScriptコードで、僕はいつもこれをpaizaで乱用しているのだが、AtCoderでこのまま使うとランタイムエラーが出る。最終的に

function func(a, b) {
 return a - b;
}
n.sort(func);

という感じで関数を外に出して何とか乗り切ったが、今後paizaと違うコーディングをしないといけないとなるとなかなかキツイ。片方でしか通らないことの理由が分からないのもまたツライ。

結果

最終結果は6問中3問正解でスコアは600点だった。スコアが何の参考になるのかは分からないが、始まる前に何もしないでたかを括るほど楽ではないし、ビビってちびるくらい難しいものでもないということが分かった。まあ競技プログラミングというのはそもそも速さを競うものだから当たり前かもしれないが。

レーティング...

今回僕は初コンテストだったのでレーティングがどういうものでどれくらいだと凄いのか全く分からなかったが、今日のコンテストで僕のレーティングは11になった。初心者の証である灰色から中級者の茶色に進級するにはレーティングが400必要らしい。。。。マジ?

ぶっつけ本番で挑んだAtCoderだったが、初心者にしては楽しめた方だったと思う。ただ僕にとってはネットワークアーキテクチャの勉強の方がやりたい事だしやるべき事でもあるので、競プロごっこは週一程度に抑えて、マイペースに茶色目指して頑張ろうと思う。

最後に、標準入出力だけは予習しよう

筆者情報

高校生。
ポートフォリオはこちら


この記事は僕のnoteからの転載です

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

GASを使っていく中で便利だと思った機能(メモ)

概要

GASを使っていろいろやっていく中で個人的に便利だと思った機能をまとめていきます。

キャッシュ

GASの処理を高速化する中で必要になって来るのがGASメソッドの呼び出し回数をいかに減らすか。。
特にカスタム関数に関しては30秒という制約が契約プラン問わずあるので複雑な処理を実装した際にとても助かりました。
https://qiita.com/golyat/items/ba5d9ce38ec3308d3757

コールバック関数

クライアント側からサーバー側のメソッドを呼び出すことが出来るのでUI操作の必要な分岐処理など使い道は様々。
https://tonari-it.com/gas-web-app-google-script-run/

V8 Runtimeのサポート

機能と言うわけではないですがこの変更が入ったことで幅が広がりました。
https://officeforest.org/wp/info/google-apps-script%e3%81%8cv8-runtime%e3%82%92%e3%82%b5%e3%83%9d%e3%83%bc%e3%83%88/

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

Vue.js 算出プロパティとメソッドの違い

はじめに

Vue.js テンプレート制御ディレクティブ まとめの続きです。

今回は算出プロパティとメソッドの違いを学習していきます。

算出プロパティcomputed

算出プロパティcomputedは、関数によって算出したデータを返す事ができるプロパティ。

例えば、以下のようにマスタッシュ構文内に複雑なロジックを書くと可読性が悪くなる。

<!-- 文字列を反転する式 -->
{{ message.split('').reverse().join('') }}

こういった複雑なロジックを実行する時に、算出プロパティを利用することが推奨される。

また、ロジックの再利用性を高めたい時などにも利用できる。

computedを利用してコードを書いてみる。

<div id="app">
  <p>
    {{ reverseMessage }} <!-- !sj.euV olleH -->
  </p>
</div>

<script>
  var app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue.js',
    },
    // 算出プロパティの定義
    computed: {
      // メソッドの定義
      reverseMessage() {
        return this.message.split('').reverse().join('')
      }
    }
  })
</script>

あれ?これmethodsと何が違うの・・・

computedmethodsそれぞれの違いを深掘りする。

算出プロパティとメソッドの3つの違い

①プロパティとメソッド

computedはプロパティなので()が不要。

methodsはメソッドなので()が必要。

<!-- computed -->
{{ reverseMessage }}
<!-- methods -->
{{ reverseMessage() }}

②getterとsetter

computedgettersetterを定義する事ができる。

methodsgetterしか定義できない。

③キャッシュ

computedはキャッシュされる。

methodsはキャッシュされない。

methodsは呼び出されるたびに関数の処理を行う

まとめ

実行したい処理の内容によってcomputedmethodsか使い分けるのが良さそう。

しかし、現状どういったケースで使い分けるかイメージが沸かない。

この辺りは、実際にアプリケーションを開発しながら身に付けるしかなさそう。

知見が深まり次第、追記していきます:writing_hand:

更新履歴

:zap:Vue.jsの基本的な使い方まとめ
:zap:Vue.jsでTO DOアプリを作る
:zap:Vue.js テンプレート制御ディレクティブ まとめ
:zap:Vue.js 算出プロパティとメソッドの違い :point_left:今ココ

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

biwascheme call/cc 非同期処理と大域脱出

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

新型コロナ感染箇所マップを作ってみる【FastAPI/PostGIS/deck.gl(React)】(データ表示編)

こちらの記事は新型コロナ感染箇所マップを作ってみる【FastAPI/PostGIS/deck.gl(React)】(データ加工編)の続きです。

上の記事をご覧になってからお読みください!

Reactの環境構築

ここまででcsvの元データをAPIからGeoJSON形式で吐き出せるようになっていると思いますので、そのデータを利用して地図上にコロナウイルス感染者の位置を表示できるようにしていきましょう!

まずはReactのプロジェクトを簡単に作成できるcreate-react-appを実行するためのNode.jsがインストールされているか確認しましょう

$node -v
v12.13.1

$npm -v
6.12.1

上記2つのコマンドを入力してバージョンの数字が表示されればインストールされています。

コマンドがないよー!なんて怒られた場合にはインストールされていませんのでNodebrewでNodeをインストールするなんかを参考にしてnodebrewのインストール→nodebrewからNode.jsをインストール→npmのインストールを行っていきましょう!

npmのインストールまで終わったらcovid_sampleに移動し、以下のコマンドでReactのプロジェクトを作成していきます。

$npm install -g create-react-app
$npx create-react-app .

これでcovid_sample自体がReactのプロジェクトになります。(コマンド最後の.はカレントディレクトリにプロジェクトを作成するという意味で、ここに何か任意の名前を入れると、covid_sample以下にその名前でディレクトリが作成されます)

$npm start

のコマンド入力後、localhost:3000へブラウザからアクセスし、以下のような画面が表示されればプロジェクトの作成成功です!

スクリーンショット 2020-04-01 8.06.28.png

deck.glの環境構築

deck.glは自動車配車サービスのUberが作成した大規模なデータセットの視覚的・探索的データ分析のためのWebGLを利用したフレームワークで、React上で動作させることを想定されています。(最近はpureJSでもちゃんと動くみたいですが)

めっちゃかっこいいです。

データさえあれば、以下のサンプルのように地図上に美しくデータを表現することができます。

スクリーンショット 2020-04-01 8.14.02.png

スクリーンショット 2020-04-01 8.16.58.png

今回はこのdeck.glを利用して地図上にデータを表示させていきましょう!

(ちなみに!!!!!今回扱うデータはただのポイントデータ(箇所ごとに緯度経度の位置情報があるだけのデータ)なので上のサンプルのようにかっこよくはなりません!!!!!!!すいません!!!!)

deck.glを利用するために以下の2つのコマンドを入力しましょう。

$npm install deck.gl
$npm install react-map-gl

下のreact-map-glはこちらもUberが作ったもので、Mapbox GL JSというWebGIS界隈では非常に有名なライブラリのReact用ラッパーになります。

今回は背景地図の表示に利用していきます。

これでいよいよ開発環境が整ったので、アプリケーションを作成していきましょう!

アプリケーションの作成

mapboxの登録とトークンの発行

mapboxとは、Google マップのようなサービスですが、JavaScriptやネイティブ用のSDKが公開されており、自由に、簡単にカスタマイズできる地図サービスのことです。

今回は背景地図としてmapboxを利用していきますので、トップページから会員登録・マイページに表示されるトークンをコピーしておいてください。

スクリーンショット 2020-04-02 7.37.09.png

背景地図の表示

srcというディレクトリが出来ているはずなのでそちらに移動しましょう。

Reactのプロジェクトでは基本的にこのsrc以下のファイル群をいじっていきます。

srcディレクトリの構成は以下のような感じになっていると思いますが、今回編集するファイルはApp.jsのみになります。

tree
.
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
├── serviceWorker.js
└── setupTests.js

ということでゴリゴリ修正していきましょう!

src/App.js
// Reactのインポート
import React from 'react';
// deck.glを利用
import DeckGL from '@deck.gl/react';
// geojsonを使うのでGeoJsonLayerを利用
import {GeoJsonLayer} from '@deck.gl/layers';
// 背景地図の表示にMapbox GL JSのラッパーを利用
import {StaticMap, Popup} from 'react-map-gl';


// 先ほど取得したトークンを文字列で格納
const MAPBOX_ACCESS_TOKEN = <MAPBOX_ACCESS_TOKEN>;

// 初期表示位置などを指定
const initialViewState = {
  longitude: 139.7212733,
  latitude: 35.6606213,
  zoom: 8,
  pitch: 45,
  bearing: 0
};

// 最近は関数ベースのコンポーネントが主流だが、サンプルに合わせてクラスベース
class App extends React.Component {
  // コンストラクターでデータを格納するstateを定義
  constructor(props) {
    super(props);
    this.state = {
      geojsonPoint: null,
    };
  }

  // コンポーネントがマウントされてから動作するメソッド
  // APIにアクセスしてデータを取得しておく
  componentDidMount() {
    fetch("http://0.0.0.0:8000/")
      .then(res => res.json())
      .then(
        (result) => {
          this.setState({
            geojsonPoint: result
          });
          console.log("result", result);
        },
        (error) => {
          console.log(error)
        }
      )
  }

  // ポップアップを表示させるためのメソッド
  _renderTooltip() {
    // hoveredObjectはホバーしている地物の情報
    const {hoveredObject} = this.state || {};
    console.log("hoveredObject", hoveredObject);
    if (hoveredObject !== undefined) {
      return (
        // react-map-glのPopupコンポーネントを利用してポップアップを表示
        <Popup
          longitude={hoveredObject.geometry.coordinates[0]}
          latitude={hoveredObject.geometry.coordinates[1]}>
          <div>
            <p>id:{hoveredObject.properties.id}</p>
            <p>年齢:{hoveredObject.properties.age}</p>
            <p>確定日:{hoveredObject.properties.fixed_data}</p>
          </div>
        </Popup>
      )
    }
  }

  // レンダリング用のメソッド
  render() {
    // コンストラクターで定義したstateからGeoJSONを取得
    const geojsonPoint = this.state.geojsonPoint;
    console.log("geojsonPoint: ", geojsonPoint);
    const layers = [
      new GeoJsonLayer({
        // 任意のid
        id: 'point_layer',
        // GeoJSONを指定
        data: geojsonPoint,
        // pointの半径
        getRadius: d => 2000,
        // 地物のカラーをRGBaで指定
        // aは透過度で0~255から指定
        getFillColor: d => [245, 36, 36, 150],
        pickable: true,
        // 地物にホバーした時に発生するイベント
        // stateを更新する
        onHover: info => this.setState({
          hoveredObject: info.object,
        })
      }),
    ];

    return (
      <>
        {/*DeckGLコンポーネントに必要な情報を渡す*/}
        <DeckGL
          initialViewState={initialViewState}
          controller={true}
          layers={layers}
        >
          {/*利用したいmapboxのスタイルとトークンを渡す*/}
          <StaticMap
            mapStyle="mapbox://styles/mapbox/dark-v9"
            mapboxApiAccessToken={MAPBOX_ACCESS_TOKEN}>
            {/*ポップアップ用の関数も入れる*/}
            {this._renderTooltip()}
          </StaticMap>
        </DeckGL>
      </>
    );
  }
}

export default App;

Reactの基本的な解説などは別記事に書こうと思っていますが、Reactでは基本的にコンポーネントの戻り値はJSXというJavaScript内にHTMLを埋め込むような記法で書いていきます。

なので、Appクラスのrenderメソッドの戻り値はHTMLのタグのようなものがずらっと並んでいます。

しかし、実際にはトランスパイルされてJavaScriptに変換されるので

<DeckGL initialViewState={initialViewState}></DeckGL>

のようにタグの中にpropsと呼ばれる変数のようなものを渡して表示を動的に切り替えることが出来ます!

その他、どういう処理を行なっているのか…という部分はコード中にコメントを書いているので参考にしてみてください!

注意する点としては

だけ気をつけてもらえればあとはちゃんと表示されるはずです!

ブラウザで表示させてみる

ここまで出来たらあとは表示させるだけです!

$npm startでサーバーを起動させてlocalhost:3000に接続してみましょう!

mov.gif

こんな感じで地図上にポイントが表示されて、ホバーすると情報が表示されるようになったと思います!!!

最後に

こんな感じで情報の収集・加工・配信・表示まで一連で行なってきましたが、データさえあれば簡単に可視化できるのが、最近のフレームワークの良いところですね!

また、データの加工段階でQGISなどのGISソフトなどを使えば都道府県ごとの人数をヒートマップで可視化などいろんなことができると思いますのでみなさんどんどんGISやっていきましょう!

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

JavaScriptのスコープについて

スコープとは

スコープとは変数を参照できる範囲のことです。今回はJavaScriptのvar,let,constについて解説します。
constは定数(再代入不可)の宣言なので特に難しく考えることはないのですが、letとvarは少し紛らわしいのでここを理解することでコードレビューの指摘が減ると思います。

今回の記事用に書いたコード

コメントに解説が書いてあるので、これだけでも分かるかもしれませんが一応ひとつずつ解説します。

function Test(){
  var hoge = 1

  // hogeは関数スコープなのでTest関数の中で使う場合はエラーにならない
  console.log(hoge)

  if (true){
    let fuga = 2
    // fugaはブロックスコープなので、ブロック内(この場合はif文の中)で使用する場合はエラーにならない
    console.log(fuga)
  }
  // fugaはブロックスコープなので、ブロック内で定義した変数をブロックの外で呼び出そうとするとエラーになる
  // Uncaught ReferenceError: fuga is not defined
  console.log(fuga)
}

Test()

// hogeは関数スコープなので、Test関数内で定義した変数を関数の外で呼び出そうとするとエラーになる
// Uncaught ReferenceError: hoge is not defined
console.log(hoge)

// constで変数を定義すると定数扱いになるため、値の再代入はできない。
// Uncaught TypeError: Assignment to constant variable.
const aaa = 3
aaa = 3

var

関数内で有効となる変数。上記のコードでは変数hogeをvarで宣言している。Test関数の外でhogeを参照使用とするとエラーとなる

let

ブロック内で有効となる関数。上記のコードでは変数fugaをletで宣言している。変数を宣言したブロックの外でfugaを参照しようとするとエラーとなる。varに比べるとスコープが狭いため、想定外の値が入りにくいというメリットがある。

const

constで宣言された場合は定数となり、再代入は不可能となる。処理内容ごとに値が変わらないことを想定している場合は定数にしておく。

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

イテレータ・ジェネレータで構文解析

はじめに

@ikiuoさんが投稿された「パターン指定型パスワード ジェネレーター (JS版) - Qiita」を拝見したところ、イテレータとジェネレータでコンパクトに作れそうな気がしたので作ってみました。
構文パーサとして使えるように、「途中で戻したり進めたりできるイテレータ」も作りました。

パターン構文

パターン構文のBNFはこんな感じでしょうか。

<syntax> ::= <item> | <item> <syntax>
<item> ::= <char> | "%" <percent>
<percent> ::= [<repeat>] ( <pattern> | "{" <brace> "}" | "[" <blacket> "]" | <char> )
<repeat> ::= ("0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9")+
<pattern> ::= <special> | <char>
<special> ::= "b"|"o"|"d"|"X"|"x"|"A"|"C"|"c"|"B"|"V"|"v"|"D"|"Q"|"q"|"Y"|"Z"|"z"|"W"|"L"|"l"
<brace> ::= <percent>+
<blacket> ::= <char>+

パターン指定例

image.png

ソースコード

percent, brace, blacket をイテレータにし、イテレータが内容文字列を次々ジェネレートしてくれれば、ジェネレートされた文字列から乱数で1文字選んで次々文字列結合してパスワード文字列の完成、という目論みです。
ジェネレータはデータを読み出すと無くなってしまうので、イテレータが呼び出される度に文字列をジェネレートするジェネレータを作って返しています。

function iter(pattern) {
    let index = 0
    return {
        [Symbol.iterator]: function() {
            return this
        },
        next: function() {
            if (index < pattern.length) {
                return { value: pattern[index++], done: false }
            } else {
                return { done: true }
            }
        },
        fetch: function() {
            return pattern[index]
        },
        skip: function() {
            index++
        },
    }
}

function charset(pattern, exclude = '') {
    function* chars() {
        let start = null
        const p = iter(pattern)
        for (let c of p) {
            if (c === '-' && start && p.fetch()) {
                const { value } = p.next()
                const end = value.charCodeAt(0)
                for (let code = start + 1; code <= end; code++) {
                    yield String.fromCharCode(code)
                }
                start = null
            } else {
                if (c === '\\' && p.fetch()) {
                    ( { value: c } = p.next() )
                }
                yield c
                start = c.charCodeAt(0)
            }
        }
    }
    return [...chars()].filter(c => !exclude.includes(c)).join('')
}

function blacket(parser) {
    let chars = ''
    let in_escape = false
    let c = parser.next().value
    while (in_escape || c !== ']') {
        chars += c
        in_escape = !in_escape && c === '\\'
        c = parser.next().value
    }
    chars = charset(chars)
    return {
        [Symbol.iterator]: function* () {
            yield chars
        }
    }
}

function brace(parser) {
    const iterators = []
    while (parser.fetch() !== '}') {
        iterators.push(percent(parser))
    }
    parser.skip()
    return {
        [Symbol.iterator]: function* () {
            for (let iterator of iterators) {
                yield* iterator
            }
        }
    }
}

const SPECIAL = {
    b: '01',
    o: charset('0-7'),
    d: charset('0-9'),
    X: charset('0-9A-F'),
    x: charset('0-9a-f'),
    A: charset('A-Za-z'),
    C: charset('A-Z'),
    c: charset('a-z'),
    B: 'AEIOUaeiou',
    V: 'AEIOU',
    v: 'aeiou',
    D: charset('A-Za-z', 'AEIOUaeiou'),
    Q: charset('A-Z', 'AEIOU'),
    q: charset('a-z', 'aeiou'),
    Y: charset('0-9A-Za-z'),
    Z: charset('0-9A-Z'),
    z: charset('0-9a-z'),
    W: charset('0-9A-Za-z_'),
    L: charset('0-9A-Z_'),
    l: charset('0-9a-z_'),
}

const SUBMODE = {
    '[': blacket,
    '{': brace,
}

function percent(parser) {
    let repeat = 0
    let c = parser.next().value
    while ('0' <= c && c <= '9') {
        repeat = repeat * 10 + parseInt(c)
        c = parser.next().value
    }
    repeat = repeat || 1
    let iterator
    if (SUBMODE[c]) {
        iterator = SUBMODE[c](parser)
    } else if (SPECIAL[c]) {
        iterator = [SPECIAL[c]]
    } else {
        iterator = [c === '\\' && parser.next().value || c]
    }
    return {
        [Symbol.iterator]: function* () {
            for (let i = 0; i < repeat; i++) {
                yield* iterator
            }
        }
    }
}

function* parse(pattern) {
    let parser = iter(pattern)
    for (let c of parser) {
        if (c === '%') {
            yield* percent(parser)
        } else {
            yield c === '\\' && parser.next().value || c
        }
    }
}

function password(pattern) {
    return [...parse(pattern)].map(s => s[Math.random()*s.length|0]).join('')
}

console.log(password('qiita-%d%X%x%x%A%C%c%c%B%V%v%D%Q%q%Y%Z%z%W%L%l'))
console.log(password('qiita-%3d'))
console.log(password('qiita-%{dXxxACccBVvDQqYZzWLl}'))
console.log(password('%8[01]'))
console.log(password('%8[S-Z]'))
console.log(password('%8[S\\-Z]'))
console.log(password('qiita%4{-c3d}'))
console.log(password('qiita%4{-Qvqd}'))
console.log(password('qiita%2{-C3{2c[13579]}}'))

実行結果

qiita-6C20VYymeUadTsKP4wTh
qiita-465
qiita-3Ce0PMeeEAeXRjqZqEX0
10101101
VWWZTTUY
ZS-ZSS-S
qiita-o350-z239-g090-k848
qiita-Lup7-Pep9-Wif2-Jix2
qiita-Fnc9yp7zn7-Uru5ia9fe7
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

日本の航空路誌における座標表示をGoogle Mapsなどで使える10進法表示に置き換えるJavaScriptスニペット

国土交通省航空局が運営しているAIS Japanから入手できる航空路誌では、ウェイポイントやATC境界などの座標が以下のような形式で記載されています。区切りはありませんが、これは60進法表記です。
image.png

eAIP の GEN 2.1.2 では、通常の書体で表記されているものは Tokyo Datum 、斜体で表記されているものはWGS84座標系であるとされています。適当にチャートを漁る限り、その多くが斜体(WGS84座標系)で示されているのでTokyo Datumはあまり考慮しなくてよいでしょう。

ということで雑に「eAIPにおけるWGS84の60進法座標表示を、Google Maps などで使える10進法表示に変換する」スニペットを置いておきます。
なお、駐機場スポットの位置などで使われている小数点を含むものについては対応していません。

function convert(aipCoord) {
    let aipLat = aipCoord.substring(0, 6);
    let aipLng = aipCoord.substring(7, 14);

    let latDeg = parseInt(aipLat.substring(0, 2));
    let latMin = parseInt(aipLat.substring(2, 4));
    let latSec = parseInt(aipLat.substring(4, 6));

    let lngDeg = parseInt(aipLng.substring(0, 3));
    let lngMin = parseInt(aipLng.substring(3, 5));
    let lngSec = parseInt(aipLng.substring(5, 7));

    let decimalLat = latDeg + latMin / 60 + latSec / 3600;
    let decimalLng = lngDeg + lngMin / 60 + lngSec / 3600;

    return { lat: decimalLat, lng: decimalLng };
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む