20200802のJavaScriptに関する記事は30件です。

Javascript覚え書き

日付の取得

今日の日付を取得

var today = new Date();

指定した日付を取得

var date = new Date( '2019/11/5 20:30' );

そこからさらに、すきな要素を取得

date.getFullYear(); // 日付の「年」を取得する(4桁)

date.getMonth();  // 日付の「月」を取得する(0 - 11)

date.getDate();    // 日付の「日」を取得する(1 - 31)

date.getDay();     // 日付の「曜日」を取得する(0 - 6)

date.getHours();   // 日付の「時」を取得する(0 - 23)

date.getMinutes(); //日付の「分」を取得する(0 - 59)

date.getSeconds(); // 日付の「秒」を取得する(0 - 59)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

配列の基礎

配列についてまとめます.
Reactの勉強をしているので,記述例がReactの構文になっています.

配列の作り方

基本的な文法は,var 配列名 = []です

記述例

import React, { Component } from 'react';

class App extends Component{
  render(){
    const array = [1,2,3,4,5,6,7,8,9,10];
    return(
      <div>
        <h1>{array}</h1>
      </div>
    )
  };
}

export default App;

image.png

ある要素を表示する方法

配列から,ある要素を表示するためには配列名[順番]です.
<注意>順番は0から数えます!

記述例

import React, { Component } from 'react';

class App extends Component{
  render(){
    const array = [1,2,3,4,5,6,7,8,9,10];
    return(
      <div>
        <h1>{array[0]}</h1>
        <h1>{array[1]}</h1>
        <h1>{array[2]}</h1>
        <h1>{array[3]}</h1>
        <h1>{array[4]}</h1>
        <h1>{array[5]}</h1>
        <h1>{array[6]}</h1>
        <h1>{array[7]}</h1>
        <h1>{array[8]}</h1>
        <h1>{array[9]}</h1>
      </div>
    )
  };
}

export default App;

image.png

配列の要素の更新・追加・削除

更新

更新するためには,配列名[順番]=更新させた要素

追加

追加するためには,
 ①配列の末尾に追加する場合,配列名.push(追加する要素)

const array = [1,2,3,4,5];
array.push(6);

image.png

 ②配列の先頭に追加する場合,配列名.unshift(追加する要素)

const array = [1,2,3,4,5];
array.unshift(0);

image.png

 ③配列の指定した場所に追加する場合,配列名.splice(無視する要素の先頭からの個数,無視しない要素のうち削除する要素の先頭からの個数,追加する要素)

const array = [1,2,3,4,5];
array.splice(2,0,3.5);

image.png

削除

削除するためには,
 ①配列の末尾の要素を削除する場合,配列名.pop()

const array = [1,2,3,4,5];
array.pop();

image.png

 ②配列の先頭の要素を削除する場合,配列名.shift

const array = [1,2,3,4,5];
array.shift();

image.png

参考

MDM web docs
Syncer

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

JS 正の整数なら前からN個、負の整数だったら後ろからN個返す

Lodashのnth関数を作成してみた

当初はすごく複雑に考えていた

var array = ["a", "b", "c", "d", "e", "f"]

const nth = (values, selectNum) => {
  if (0 < selectNum) {
    return values[selectNum]
  } else {
    let absolute = Math.abs(selectNum)
    let newArray = []

    for (let i = 0; i < values.length; i++) {
      const array = values.length - (i + 1)
      newArray.push(values[array])
    }

    return newArray[absolute - 1]
  }
}

console.log(nth(array, 3))
// => d

console.log(nth(array, -5))
// => b

もっとシンプルに記述できた

const nth = (values, num) => {
  return 0 < num ? values[num] : values[values.length + num]
}

console.log(nth(array, 3))
// => d

console.log(nth(array, -5))
// => b
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【javascript】テトリス作成に向けて

テトリス作成に向けて調べた関数などを書いていきます。

配列内の値を一括で変更する

Array.prototype.fill()

メソッドは、配列中の開始位置から終了位置までの要素を固定値で設定します。その際、終了位置は含まれません。
出典:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/fill

構文

arr.fill(value[, start[, end]])
引数 説明
value 配列に設定する値
start 開始する位置。デフォルトは0
end 終了する位置。デフォルトはthis.length

bord.forEach((item) => {
    bord[i].fill(0)
  });

これでbordの配列の値をすべて0にできる。

2つの配列を比較する

Array.prototype.filter()

構文

let newArray = arr.filter(callback(element[, index, [array]])[, thisArg])
callback引数 説明
element 現在処理中の要素
index 現在処理中のインデックス
array filterが実行されている配列
thisArg callbackを使う時にthisとして使用する値

返り値

フィルターされた配列

-0(マイナスゼロ)に遭遇

console.log(-1*0)//-0

このような結果に遭遇した

これについてMDNではこう述べられている

Number 型には、2 種類の表現を持つ数値がひとつだけあります。それは 0 であり、-0 および +0 で表します ("0" は +0 の別名です)。実用上、影響はほとんどありません。例えば、+0 === -0 は true です。ただし、ゼロ除算を行った場合は違いに気づくでしょう
出典:JavaScript のデータ型とデータ構造

+0 === -0//true

これがtrueになるなら問題ないかと思いスルー

絶対値

Math.abs()

NodeListをforEachで使えるようにする

Array.from(Nodelist).forEach()

forEachではreturnは使えない

forEachでreturnで処理を終わらせようとしたところ最後まで処理をしていることがわかった。

配列のディープコピー

let arr=[[0,0],[0,1]]
let arr2=JSON.parse(JSON.stringify(arr));

数字の四捨五入

// 切り捨て
var a = Math.floor( 1.5 ) ;

// 切り上げ
var b = Math.ceil( 1.5 ) ;

// 四捨五入
var c = Math.round( 1.5 ) ;

参考

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

transitioned

transitionedとは

HTMLベースのページで、javascriptによってCSSの切り替えが起こったときのことを表す
例)

key.addEventListener('transitioned', removeTransition)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】レスポンシブな条件分岐にはwindow.matchMediaを使う

JavaScriptを書いていて良く使用するレスポンシブな条件分岐は下記2つのパターンだと思います。どちらもwindow.matchMediaを使えば簡単に実装することができます。

  • デバイス幅が〇〇px以上と以下で処理を分岐したい
  • 特定のデバイス幅を通過したときに処理を実行したい

今回はこちらの2つの頻出パターンについて紹介します。

デバイス幅が〇〇px以上と以下で処理を分岐したい

こちらの条件分岐は、PCとスマホで処理を分けたいときなどに重宝します。一番良く使いますね。

JavaScript
const matchMedia = window.matchMedia('(max-width:1000px)');

if (matchMedia.matches) {
  // 1000px以下で行う処理
} else {
  // 1001px以上で行う処理
}

ちなみにif (!matchMedia.matches)とすることで、条件にマッチしないときといった条件分岐を行うこともできます。

特定のデバイス幅を通過したときに処理を実行したい

特定のデバイス幅を通過したときに何か必ず実行したい処理があるときなんかに便利です。

JavaScript
const matchMedia = window.matchMedia('(max-width:1000px)');

// 1000px通過時にmyFuncを実行する
matchMedia.addListener(myFunc);

Twitterアカウント

Twitterも更新していますので、よかったらフォローお願いします!
Twitterアカウントはこちら

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

JS 配列の最初以外を返す

Lodashのtail関数を作成してみた

メソッド使用せずに作成した場合

const tail = (array) => {
  const tailNewArray = []
  for (let i = 1; i < array.length; i++) {
    tailNewArray.push(array[i])
  }
  return tailNewArray
}

console.log(tail([2, 4, 5]))
// => [4, 5]

console.log(tail([1]))
// => []

filterメゾッドを使用した場合

const tail = (numbers = []) => {
  return numbers.filter((value, index, array) => {
    return array.indexOf(value) !== 0
  })
}
console.log(tail([2, 4, 5]))
// => [4, 5]

console.log(tail([1]))
// => []
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Audio/Video DOM のcurrentTime1とは

Audio/Video DOMとは

bodyブロックに書く音声や映像の情報。
例)
<audio data-key="76" src="sounds/tink.wav"></audio>

current Timeの使い方

    window.addEventListener('keydown', function (e){
      const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
//再生時間を取得
      var time =audio.currentTime; 
    });

例えば

audio.currentTime=0;

とすると
再生時間が最初に戻る

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

javascriptで回帰直線を求める

回帰直線とは

組となるデータの、中心的な分布傾向を表す直線。
最小二乗法( least squares method )によって求められます。

最小二乗法の式

wiki_lsm.png

?最小二乗法(wikipedia)より引用

これを、javascript( ES6 )で関数化します。
Σreduceで表現しています。

// 回帰直線を求める(最小二乗法)
const lsm = coordinates => {
  const n = coordinates.length;
  const sigX = coordinates.reduce((acc, c) => acc + c.x, 0);
  const sigY = coordinates.reduce((acc, c) => acc + c.y, 0);
  const sigXX = coordinates.reduce((acc, c) => acc + c.x * c.x, 0);
  const sigXY = coordinates.reduce((acc, c) => acc + c.x * c.y, 0);
  // a(傾き)を求める
  const a = (n * sigXY - sigX * sigY) / (n * sigXX - Math.pow(sigX, 2));
  // b(切片)を求める
  const b = (sigXX * sigY - sigXY * sigX) / (n * sigXX - Math.pow(sigX, 2));
  return { a, b };
}

?動作確認

ドライバを作成して呼び出してみます。

  const coordinates = [
    { x: 200, y: 100 },
    { x: 250, y: 150 },
    { x: 300, y: 150 },
    { x: 312, y: 200 },
    { x: 330, y: 210 },
    { x: 100, y: 80 },
    { x: 110, y: 40 },
  ]

  const { a, b } = lsm(coordinates)
  console.log(a, b)

結果

0.6227602344112315 -9.665985075256124

?グラフを書いて確認

canvasにグラフを描画して確認してみました。
青のドットがサンプルデータ、赤が回帰直線です。
result_lsm.png

確認に使ったソースコードは以下です。

html

<html>
  <head></head>
  <body>
    <canvas id="graph" width="500" height="500"></canvas>
    <script type="text/javascript" src="main.js"></script>
  </body>
</html>

javascript

main.js
// 回帰直線を求める(最小二乗法)
const lsm = coordinates => {
  const n = coordinates.length
  const sigX = coordinates.reduce((acc, c) => acc + c.x, 0)
  const sigY = coordinates.reduce((acc, c) => acc + c.y, 0)
  const sigXX = coordinates.reduce((acc, c) => acc + c.x * c.x, 0)
  const sigXY = coordinates.reduce((acc, c) => acc + c.x * c.y, 0)
  // a(傾き)を求める
  const a = (n * sigXY - sigX * sigY) / (n * sigXX - Math.pow(sigX, 2));
  // b(切片)を求める
  const b = (sigXX * sigY - sigXY * sigX) / (n * sigXX - Math.pow(sigX, 2));
  return { a, b }
}

const canvas = document.getElementById('graph');
if (canvas.getContext) {
  const ctx = canvas.getContext('2d');

  // x軸の描画
  ctx.beginPath();
  ctx.moveTo(100, 0);
  ctx.lineTo(100, 500);
  ctx.stroke();
  // y軸の描画
  ctx.beginPath();
  ctx.moveTo(0, 400);
  ctx.lineTo(500, 400);
  ctx.stroke();

  const coordinates = [
    { x: 200, y: 100 },
    { x: 250, y: 150 },
    { x: 300, y: 150 },
    { x: 312, y: 200 },
    { x: 330, y: 210 },
    { x: 100, y: 80 },
    { x: 110, y: 40 },
  ]
  // サンプルデータの点を描画
  ctx.fillStyle = "rgb(0, 0, 255)"
  coordinates.forEach(c => {
    ctx.fillRect(c.x + 100, -c.y + 400, 3, 3)
  })

  // 回帰直線の描画  
  const { a, b } = lsm(coordinates)
  ctx.strokeStyle = "rgb(255, 0, 0)";
  ctx.beginPath();
  ctx.moveTo(-100 + 100, -(-100 * a + b) + 400);
  ctx.lineTo(400 + 100, -(400 * a + b) + 400);
  ctx.stroke();
}

コードのダウンロードはここ

以上。

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

いままでのリード・ライトに、もうひとついれたパパ・ラッチ

ラッチ機構をいれることにより、ビジネスの範囲にまで進めたんだろうね。

あと、もう一歩だ。

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

【Vue.js】CDN版でも単一ファイルコンポーネント(.vue)を使いたい!

こんにちは。

CDN版ライブラリの使用については皆さん賛否両論あると思いますが、私は容認派です。
手軽に使える便利さは正義!

そんなわけで、ビルド不要なCDN版Vue.jsでも単一ファイルコンポーネントシステム(.vueファイル)を実現したくね!?と思ったので色々と試してみたお話です。

そもそもそんなことできるの?

できました。
しかし一筋縄では行かず、クライアントサイドで多少のゴリ押しが必要となります。
ゴリ押すためには、以下のキーポイントを押さえる必要があります。

非同期通信

いわゆるAjaxと呼ばれるやつです。
XMLHTTPRequestfetchなど色々ありますが、ここではaxiosという便利な非同期通信ライブラリを使用します。

非同期コンポーネントとミックスイン

Vue.jsは、非同期でコンポーネントを追加できる、非常に柔軟な機構を持っています。
これらを使用し、非同期通信で取得した.vueファイルをパースし、既存のコンポーネントへ追加します。

VueRouter

Vue.jsで仮想ルーティングを実装するためのプラグインです。
実はVueRouterもCDN版が存在し、ビルド不要で仮想ルーティングを実装できます。
(更に言うとステート管理プラグインのVuexもCDN版が存在します!)

VueRouterと単一ファイルコンポーネントを組み合わせ、ビルドシステムさながらのページを構築することができます。

サンプルコード

  • /index.html: メインページ
  • /component.vue: Vueコンポーネントファイル
  • /paths.json: 仮想パスとコンポーネントファイルの物理パスを列挙したマッピングデータ

これら3個のファイルのみで、Vue.jsアプリケーションを構築してみます。
UIはVuetifyを使用しました。

なぜマッピングデータが必要かというと、静的配信においてクライアントはサーバーがどのファイルを配信しているかを知る術がないからです。
マッピングデータを用意してやることにより、クライアントがコンポーネントファイルの場所を解決できるようになります。

最初のうちはマッピングデータをメインページのJavaScript内で記述しても問題ないと思いますが、拡張性を考えると外へ書き出しておいたほうが良いかなと思います。

index.html

index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="height=device-height, width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">

        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vuetify@latest/dist/vuetify.min.css">
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">

        <script src="https://cdn.jsdelivr.net/npm/vue@latest/dist/vue.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/vue-router@latest/dist/vue-router.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/vuetify@latest/dist/vuetify.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/axios@latest/dist/axios.min.js"></script>

        <title>Vue</title>
    </head>

    <body>
        <div id="vue">
            <v-app>
                <v-app-bar app dark height="52" color="secondary">
                    <v-toolbar-title>Vue</v-toolbar-title>
                </v-app-bar>

                <v-main>
                    <router-view></router-view>
                </v-main>
            </v-app>
        </div>
    </body>

    <script>
        const loadComponent = axios.create({
            method: "get",
            baseURL: ".",
            responseType: "text",
            transformResponse(data){
                const doc = new DOMParser().parseFromString(data, "text/html");

                const template = doc.head.getElementsByTagName("template")[0]?.innerHTML;
                const script = doc.head.getElementsByTagName("script")[0]?.innerText
                const style = doc.head.getElementsByTagName("style")[0];

                if(style){
                    document.head.appendChild(style);
                }

                return {
                    template(){
                        return template ? template : "";
                    },
                    script(){
                        return script ? new Function(script.trim().replace(/^(export\s+default|module\.exports\s*?=)\s*?/i, "return"))() : {};
                    }
                };
            }
        });

        const loadMap = axios.create({
            method: "get",
            baseURL: "./components",
            responseType: "json",
            transformResponse({parents, paths}){
                return Object.entries(paths).map(([virtual, physical]) => [`${parents.virtual}${virtual}`, `${parents.physical}${physical}`]);
            }
        });

        const vue = (async()=>{
            const maps = [
                "/paths.json"
            ];

            const paths = [];
            for await(const {data} of maps.map(map => loadMap(map))){
                paths.push(...data);
            }

            return new Vue({
                el: "#vue",
                vuetify: new Vuetify(),
                router: new VueRouter({
                    routes: paths.map(([virtual, physical])=>{
                        return {
                            path: virtual,
                            async component(res){
                                const {data} = await loadComponent(`${physical}.vue`);
                                res({
                                    template: data.template(),
                                    mixins: [data.script()]
                                });
                            }
                        };
                    })
                })
            });
        })();
    </script>
</html>

コンポーネントローダー

最初にaxios.create()で、コンポーネントファイルをロードするためのaxiosインスタンスを作ります。
以後、このインスタンスをコンポーネントローダーと呼称します。

レスポンスデータはtransformResponse()でVueコンポーネントとして使えるよう加工します。

加工する内容は、まずVueコンポーネントファイルをテキスト形式で取得し、DOMツリーへと変換します。
これは、Vueコンポーネント内の<template>/<script>/<style>を分離するためで、正規表現マッチによるタグ検出も不可能ではないですが、より簡単で正確なDOMParserを使用します。

なお、VueコンポーネントはHTMLとして解析されるため<template>/<script>/<style>は自動的にDOMツリーの<head>内へ移動されます。

テンプレート文字列<template>はそのままinnerHTMLで中身を返します。
問題はJavaScript文字列の<script>で、これは少しテクニカルです。

JavaScript文字列の評価にはしばしばeval()が用いられますが、このeval()は速度面でも安全面でも良くないという話は周知の事実かと思います。

そこで、同じくJavaScript文字列から評価を行える、関数コンストラクタnew Function()を使います。
関数を作成する場合、通常であればfunction xxx(){}と書きますが、関数コンストラクタを用いることでも作成できます。

関数コンストラクタは、中身となるJavaScript文字列を引数として受け取り、それを評価し関数オブジェクトとして返します。
eval()とは違い、評価結果が関数スコープ内へ封じ込められるため安全という訳です。

関数コンストラクタは関数を返すためnew Function()()としてやれば、評価した関数をそのまま即時実行することもできます。

なお、評価の直前でJavaScript文字列の先頭export defaultまたはmodule.exportsreturnへと書き換えています。
これは、コンポーネントオブジェクトをexportへ出力するのではなく、関数の返り値とするためです。

この時の動作を表すと、以下のようになります。

// "return" へ書き換えた後に関数コンストラクタの引数へ代入
// "export default{data(){},...}" => "return{data(){},...}"
return new Function("return{data(){},...}")();
// => {data(){},...}

// 上記の関数コンストラクタの評価結果(等価)
return (function(){
    return {
        data(){
        },
        ...
    };
})();
// => {data(){},...}

この、評価後に返されるコンポーネントオブジェクトを後述のミックスインとして追加している訳です。

もし書き換えなければ、コンポーネントオブジェクトは正体不明のexportへ出力されることになってしまいます。

なお、テンプレートやスクリプトの処理結果を、オブジェクトプロパティではなく関数として返しているのは、参照渡しではなく値渡しするためです。

<style>は問答無用でドキュメント内へ挿入しています。
(将来的に乱数もしくはハッシュ値を取ってscoped対応もできれば良いなと考えています)

マップローダー

再びaxios.create()を使い、マッピングデータをロードするためのaxiosインスタンスを作成します。
以後、このインスタンスをマップローダーと呼称します。

このレスポンスデータもtransformResponse()でマッピングデータとして使えるよう加工します。

マッピングデータは 後述 のようなデータ構造となっています。
parentsプロパティで仮想パスと物理パスそれぞれに親パスを宣言できるため、親パスとpathsオブジェクト内の子パスを結合してあげます。

返り値はArray<["仮想パス", "物理パス"]>のような連想配列とすることで、その後のイテレーション処理を簡単にしています。

非同期コンポーネント

まず、VueRouterのパスが非同期で取得されるマッピングデータへ依存しているため、Vueインスタンス全体をasync即時関数でラップします。

なお、複雑なパスの構築へ備え、複数のマッピングデータファイルを結合できるような作りになっています。
マップローダーでマッピングデータを取得し終えたら、Vueインスタンスの構築へ移ります。

VueRouterのpathプロパティへ仮想パスを、コンポーネントローダーへ物理パスを渡すことで、それぞれマッピングの整合をとっています。

Vue(VueRouter)のcomponentプロパティは、コンポーネント内容を直接記載する場合はオブジェクトとしますが、コンポーネント内容が非同期な場合は、引数resolveで解決されるPromiseを返すcomponent()関数となります。

templateプロパティには、コンポーネントローダーの返り値template()でテンプレート文字列を渡します。
mixinsプロパティには、コンポーネントローダーの返り値script()でコンポーネントオブジェクトを渡します。

component.vue

component.vue
<template>
<div>
    <v-btn>foo</v-btn>
    <v-btn>{{test}}</v-btn>
</div>
</template>

<script>
export default {
    data(){
        return {
            test: "aaa"
        };
    }
};
</script>

普通のコンポーネントファイルと同様に記述していきます。
ただ実際にロードされるときは、上述の通り先頭がexport defaultまたはmodule.exportsからreturnへ自動で書き換えられます。

paths.json

paths.json
{
    "parents": {
        "virtual": "",
        "physical": ""
    },
    "paths": {
        "/": "/component"
    }
}

pathsプロパティで"仮想パス":"物理パス"のようにマッピングしていきます。
なおparentsプロパティで一括して親パスを設定できるため、実際のパスはparents+pathsとなります。

仮想パスについては:idのようなVueRouterの動的ルートマッチング機能も使えます。
物理パスに拡張子を含めるか含めないかは好みですが、今回は実質.vue以外には成り得ないため、ここでは記述しないほうがミスは減るかなと思います。

まとめ

だいぶパワープレイになりましたが、無事CDN版Vue.jsでも単一コンポーネントシステムを実現することができました。

と言うかこれもう完全に人力ビルドシステムだなおい

もっと良い方法があるよ!とか参考になった!とかありましたらコメント頂ければ幸いです!

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

コールバック関数

引数に関数を渡す。
Javaから始めた自分としては考えられない。

var callBackTest1 = (callback) => {
    console.log("1");
    callback();
};

var callBackTest2 = () => {
  console.log("2");
}

// 呼び出し
callBackTest1(callBackTest2);
// "1"
// "2"

上記を無名関数に置き換えることもできる。

callBackTest1(function() {
    console.log("2");
});    

引数がある関数を呼ぶ場合

var callBackTest1 = (callback, name) => {
    console.log("1");
    callback(name);
};

var callBackTest2 = (name) => {
  console.log(name);
}

callBackTest1(callBackTest2, "hoge");
// 1
// "hoge"

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

Objectのコピー(deep copy)

JSON.stringify() メソッドと、JSON.parse() メソッドを使う。

var obj = {name : "yamada", id : "001"};
var copyObj = JSON.parse(JSON.stringify(obj));
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ド初心者がWEBデザイナーになる話。 #1

超初心者が独学でWEBデザイナーを目指す話。

一人で心が折れそうなのと
どれくらいまでやったかよくわかんなくなってきたので整理のために書こうと。
道標というよりかは必死にあがいてる様を垂れ流すだけの場所とする。

今のところやった事

  • htmlとCSSの本でサンプルサイト作成 →それを自分用にアレンジ
  • Javascriptを甘噛み →なんとなくそこはかとなく書いてあることはわかる(詳しくはわかんない)
  • jQueryを甘噛み →なんとなく以下略
  • 簡単そうなサイトを模写をする(トップページのみ)
  • 1ページのオリジナルサイト作成 →CSSおしゃれ装飾やJsのコピペしてちょこっと変えたりして使ってみる
  • WordPress甘噛み中(本使って勉強中) →現状なんとなく以下略な感じ

WordPress難しすぎて脳みそが爆発しそう。
頑張らないといけない…。
正直こんな勉強方法であってるかどうかもわからない。
確実に言えるのは何となくはわかる…ただそれだけでは意味が無いので確実に理解そして応用できるようにならねば。。。

道は険しい、ゲボでそう()

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

〔JavaScript〕未来の自分が読みやすいコードにするために出来る事

はじめに

私はJavaScriptを勉強し始めて3ヶ月経ちました。
難しい問題も解けるようになり始めましたが、深いネストが多く見直した時に何をしているのかわからないコードを書いてしまいます。
深いネストの解消の仕方調べても難しそうな関数がたくさんで今すぐに改善するのは大変そう、、、、と思ってしまいました。
今回は、読みやすいコードにするためにする方法をたくさん調べ、今からでも意識して変えられる方法をまとめて書きたいと思います。

マジックナンバーには名前をつける

ハードコーディングされている謎の数字のことをマジックナンバーと言います。

// この「50」は何?
const pages = Math.ceil(records.length / 50);

マジックナンバーは混乱させる原因になります。できるだけ値には名前をつけるようにしましょう。

const maxPage = 50;
const pages = Math.ceil(records.length / maxPage);

とすることで最大のページ数が50であることがわかります。

もちろん名前をつけなくてもいい数字もあります。
例えば角度をラジアンに変換するとき、「180」に名前をつける必要はないと思います。

const radian = degree * Math.PI / 180;

識別子を短くしない

識別子(関数名、変数名など)をむやみに短くしないというのも大事です。名前が省略されていると、本来の名前を推測する必要が出てきます。そうすると無駄な時間がかかりますし、最悪の場合は全く違う理解をしてしまうことがあります。
私の場合、関数の意味あっているものをつけるだけではなく、似たような意味を持つ関数の時に違いがわからないような名前をつけてしまうので気をつけたいです。

否定形は、ほどほどに

名前に否定形はあまり入れないほうがいいです。
何か処理するのは表示するとき?しないとき?と混乱します。

関数名のおすすめサイト

プログラミングでよく使う英単語のまとめ【随時更新】

目的を名前にする

こちらも名前関連で、うっかりすると、関数名で「手段」を名前につけてしまうことがあります。
後から手段が変わったときに実態と異なる名前になってしまい、バグのもとです。
名前には「目的」をつけましょう。

コメントを書く

どうわかりやすくすればいいかわからなくなった時は私はだいたいコメント書いてしまいます。
初歩的な事柄であるわりに効果が大きいのがコメントです。コメントは、コードを読む人が意味を理解するための大きな助けになります。

深いネストの解消

関数を使うネストの解消は私には難しく少し勉強しないと使えないかなと思ったので、すぐできそうな解消の仕方を紹介します。

if(...){
    if(...){
        if(...){
            処理...
        }else{
            エラー処理①...
        }
    }else{
        エラー処理②...
    }
}else{
    エラー処理③...
}

if文が3つネストしています。
この場合はエラー処理をifで定義してネストを解消することができます。

if(...){
    エラー処理③
    return or exception
}

if(...){
    エラー処理②
    return or exception
}

if(...){
    エラー処理①
    return or exception
}

  処理...

他にも改善できる書き方があります。

数珠つなぎの条件
if($a == 1){
  if($b == 2){
    // 何らかの処理
  }
}

こうやって繋げていくのはやめて

if($a == 1 && $b ==2){
  // なんらかの処理
}

こうできるといいと思います。

最後に

紹介した他にもたくさん良くする方法はあります。
もっとこうすると良いんじゃないかっと思ったらコメントイダ抱けると嬉しいです。

参考リンク

他人に読んでもらうJavaScriptコードを書くために
読みやすいコードを書くために
新人プログラマに知ってもらいたいメソッドを読みやすく維持するいくつかの原則
【プログラミング】ネストの減らし方 〜可読性UPのシンプルな原則〜

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

ES6、ES5メモ

英語で書いた練習問題の自分用メモです。
ES5, ES6の場合ですと、以下の通り重要なポイントがあります。

// change everything below to the newer Javascript!

// let + const
let a = 'test';
const b = true;
const c = 789;
a = 'test2';


// Destructuring
// var person
const person = {
    firstName: "John",
    lastName: "Doe",
    age: 50,
    eyeColor: "blue"
};

const { firstName, lastName, age, eyeColor } = person;
// var firstName = person.firstName;
// var lastName = person.lastName;
// var age = person.age;
// var eyeColor = person.eyeColor;


// Object properties
// var a = 'test';
// var b = true;
// var c = 789;

// var okObj = {
//     a: a,
//     b: b,
//     c: c
// };
const a = "test";
const b = true;
const c = 789;

var okObj = {
    a, b, c
}



// Template strings
// var message = "Hello " + firstName + " have I met you before? I think we met in " + city + " last summer no???";
const message = `Hello, ${firstName} Havae I met you before? I think we met in ${city} last summer NO???`




// default arguments
// default age to 10;
// function isValidAge(age) {
//     return age
// }
const isValidAge = (age = 10) => age;
// 这里使用这个const定义函数的含义是,不允许这个变量继续编程别的东西了。他只能指向这个函数。


// Symbol
// Create a symbol: "This is my first Symbol"

// Arrow functions
// function whereAmI(username, location) {
//     if (username && location) {
//         return "I am not lost";
//     } else {
//         return "I am totally lost!";
//     }
// }

const whereAmI = (username, location) => {
    if (username && location) {
        return "I am not lost";

    } else {
        return " I am totally lost";
    }
}

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

配列のコピー

変数を代入する

変数を代入した場合、コピーとはならず同じデータを参照されるようになる。
そのため片方で行った変更がもう片方に影響を与えてしまう。

var a = [1, 2, 3];
var b = a;
// 変数bに対して行った値の変更が変数aにも影響を与えてしまう。
b[0] = 9;
console.log(b);
// [9, 2, 3]
console.log(a);
// [9, 2, 3]

コピーには種類があり、浅いコピーと深いコピーがある。
他の変数に影響を与えないようにしたい場合、深いコピーをする必要がある。

slice(浅いコピー)

var array = [
  {name: "yamada"},
  {name: "tanaka"},
];

var copyArray = array.slice();
copyArray[0].name = "sasaki";
console.log(array);
// {name: "sasaki"},{name: "tanaka"}
console.log(copyArray);
// {name: "sasaki"},{name: "tanaka"}

concat(浅いコピー)

var array = [
  {name: "yamada"},
  {name: "tanaka"},
];

var copyArray = array.concat();
copyArray[0].name = "sasaki";
console.log(array);
// {name: "sasaki"},{name: "tanaka"}
console.log(copyArray);
// {name: "sasaki"},{name: "tanaka"}

JSON.stringifyを使用する(深いコピー)

この方法では、Date オブジェクトや function など 文字列化すると壊れるオブジェクトが含まれていると正しくコピーできないようだ。

var array = [
  {name: "yamada"},
  {name: "tanaka"},
];

var copyArray = JSON.parse(JSON.stringify(array));
copyArray[0].name = "sasaki";
console.log(array);
// {name: "yamada"},{name: "tanaka"}
console.log(copyArray);
// {name: "sasaki"},{name: "tanaka"}

他にはjQueryのExtend関数やlodashのcloneDeepを使うと
実現できるようだ。

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

JavaScriptからletを絶滅させ、constのみにするためのレシピ集

はじめに

本記事では、constこそが唯一神であることを証明したあと、letを使いがちな場面でいかにしてconstを使うかをまとめていきます。なお、ES2017までの基本構文(async/await)とJSプログラマ御用達の便利メソッド詰め合わせであるLodashのみを使用します。導入コストほぼ0で、チーム開発でも個人開発でも、明日からすぐに実践できるものを紹介していきます。

対象者

初心者(文法覚えたて。letとconstの違いはわかる)~中級者(Promiseを理解し、async/awaitやthen/catch, try-catchが使える)くらいを想定しています。

letに対する誤解

「varは使っちゃだめ! letやconstを使いましょう!」という言い回しをよく聞きます。
varは関数全体にスコープが漏れ出してしまうのが理由です。

varはダメという主張自体は間違いないと思うのですが、「letやconstを使いましょう」というと、letとconstが同等の地位であるかのような印象を初学者の方に与えてしまいます。
違います。

constこそが唯一神であり、それと比べてしまえばletとvarの差など微々たるもの


なのです。

constが唯一神である理由

4歳娘「パパ、constしか使わないで?」
こちらの記事が非常にわかりやすくまとまっております。
この記事でご説明いただいている通り、letは「いつ再代入されるかわからない」という恐怖を読み手に与えてしまいます。
これに加えて、うっかりグローバル変数を爆誕させてしまう危険性があります。

実は危ないコード
const main = () => {
  let c = 1
  // 何らかの処理
  c = 2
  console.log(c) // 2
}

main()
console.log(c) // エラー

さて、let c = 1の行が消えたらどうなるでしょうか

グローバル変数爆誕の瞬間
const main = () => {
  // 何らかの処理
  c = 2 // var, let, const何もついてないのでグローバル変数が爆誕
  console.log(c) // 2
}

main()
console.log(c) // 2

まあ、use strictつけたり、eslintのno-implicit-globalsを設定すればグローバル変数の爆誕は防げますが。
いずれにせよ$const > let$であることが説明できました。冒頭で$let > var$は証明済みのため、まとめると$const > let > var$です。これでconstが唯一神であることが証明できました$Q.E.D.$

letをconstに変えるレシピ集

letを使いがちないくつかの場面に対して、constに変える方法を伝授していきます。

環境

  • ES2017(async/awaitが必要)
  • Lodash 4.17.15
import _ from 'lodash'

している前提です。
 また、awaitをトップレベルで使っている際は以下のようにasync関数の一部を切り出しているものとしてお考えください(そもそもNode 14.3.0からtop level await使えるから許してくれ)

async function main() {
  /** サンプルコード****************
   *                           *
   *****************************/
}
main()

これらを念頭に置いて以下のサンプルコードをお読みください。

初級

10回繰り返したいfor文

これについてはfor文の中だけのスコープになるので許しても良い気もしますが、例外を認めるとletが根絶できません。

before
for (let i = 0; i < 10; i++) {
  console.log(i)
}
after
_.range(10).forEach(i => {
  console.log(i)
})

PythonのrangeみたいなやつをLodashは提供してくれています。
(ちなみにこの例はもう少しスマートにできます。)

スマートにできる
_.range(10).forEach(console.log)

数値配列の合計値を算出

before
const arr = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
let sum = 0
for (let i = 0; i < arr.lenth; i++) {
  sum += arr[i]
}
console.log(sum)
after
const sum = _.sum(arr)
console.log(sum)

素のJSでもreduceを使えば合計は求められますが、こちらのほうがわかりやすいはずです。

オブジェクトの配列の合計値を算出

before
const users = [{ name: 'person1', age: 10 }, { name: 'person2', age: 20 }, { name: 'person3', age: 30 }]
let sumOfAge = 0
for (const user of users) {
  sumOfAge += user.age
}
console.log(sumOfAge)
after
const sumOfAge = _.sumBy(users, 'age')
console.log(sumOfAge)

LodashのsumByの使い所です。

if文

before
let tax
if (持ち帰り) {
  tax = 0.08
} else {
  tax = 0.1
}
after
const tax = 持ち帰り ? 0.08 : 0.1

三項演算子を使ってはいけないと誰かに言われたら、先ほどのconstが唯一神である証明でも見せて黙らせましょう。三項演算子はネストさせない限りは使っても問題ありません。

じゃあswitch文どうするのよ

before
let message
switch (response.status) {
  case 200:
    message = 'OK'
    break
  case 204:
    message = 'No Content'
    break
  // ...省略
}
console.log(message)
after
const getMessageByStatus(status) => {
  switch (status) {
  case 200:
    return 'OK'
  case 204:
    return 'No Content'
  // ...省略
  }
}

const message = getMessageByStatus(response.status)

関数に切り分けましょう。breakする必要もなくなって行数が減りました。
(ちなみにこの例だとオブジェクトで宣言しといたほうが良い)

オブジェクトで宣言しとく
const statusToMessage = {
  200: 'OK',
  204: 'No content'
}
const message = statusToMessage[response.status]

中~上級

try-catchとの兼ね合い

before
let response
try {
  response = await requestWeatherForecast() // 天気予報APIを叩く
} catch (err) {
  console.error(err)
  response = '曇り' // APIから取得できなかった場合は適当に曇りとか言ってごまかす
}
console.log(response)
after
const response = await requestWeatherForecast().catch(err => {
  console.log(err)
  return '曇り'
})

Promiseのcatchメソッド内でreturnした値はawaitを通せば外界の変数に代入することができます

例外catchしたら早期returnしたいんだが

before
let response
try {
  response = await requestWeatherForecast() // 天気予報APIを叩く
} catch (err) {
  console.error(err)
  return
}
console.log(response)
after
const shouldReturn = Symbol('shouldReturn') // 普通に文字列の'shouldReturn'でも良いか?
const response = await requestWeatherForecast().catch(err => {
  console.error(err)
  return shouldReturn
})
if (response === shouldReturn) return
console.log(response)

先ほどの応用です。ちなみにこの方法は筆者の思いつきで、このようなコードを書いている人を観測したことはありません。

リトライ処理

before
// 天気予報APIを叩く。エラーが出たら10回までリトライする
const MAX_RETRY_COUNT = 10
let retryCount = 0
let response
while(retryCount <= MAX_RETRY_COUNT) {
  try {
    response = await requestWeatherForecast() // 天気予報APIを叩く
    break
  } catch (err) {
    console.error(err)
    retryCount++
  }
}
console.log(response)
after
// 与えられた関数をmaxRetryCount回までリトライする関数。
const retryer = (maxRetryCount, fn, retryCount = 0) => {
  if (retryCount >= maxRetryCount) return undefined

  return fn().catch(() => retryer(maxRetryCount, fn, retryCount + 1)) // retryCountを1増やして再帰呼び出し
}

const response = await retryer(MAX_RETRY_COUNT, requestWeatherForecast)

retryerのようなラップ関数をつくりましょう。

番外編(不変じゃないconst)

有名な話ではありますが、constは再代入できないだけで、constで宣言した配列に要素を追加したり、constで宣言したオブジェクトにプロパティを追加することはできてしまいます。

constでも変更を加えられる例
const arr = []
arr.push(1) // arr: [1]
const obj = {}
obj.a = 1 // obj: { a: 1 }

これらの行為はconstという唯一神をletと同じ地位まで貶める愚行です。以下で、配列やオブジェクトを変更しがちな例を紹介し、その代替案を紹介します。

配列から条件に合うものだけ抜き出す

before
// 偶数だけを抜き出す
const arr = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
const result = []
for (const n of arr) {
  if (n % 2 === 0) result.push(n) // 愚行
}
console.log(result)
after
const result = arr.filter(n => n % 2 === 0)

filterを使いましょう。ちなみにfilterは宣言しておいた関数を渡すと可読性が高まります。

可読性が高い
const isEven = num => num % 2 === 0 // 関数を用意
const result = arr.filter(isEven)

変数がundefinedじゃないときだけオブジェクトに追加

before
// トークンがundefinedじゃなかったら、Authorizationヘッダーを追加
const header = {
  'Content-Type': 'application/json'
}
if (token !== undefined) header.Authorization = `Bearer ${token}` // 愚行
after
const header = {
  'Content-Type': 'application/json',
  ...(token === undefined ? {} : { Authorization: `Bearer ${token}` })
}

三項演算子と...のスプレッド構文の組み合わせです。空のオブジェクトをスプレッド構文で展開すると消えるというテクニックは便利です。

オブジェクトの値部分に処理を加える

before
// 値部分をNumberに変換し、値が偶数のところだけ抜き出す。
const obj = { a: '1', b: '2', c: '3', d: '4', /* ... */ }
cosnt result = {}
for (const [key, value] of Object.entries(obj)) {
  const number = Number(value)
  if (number % 2 === 0) {
    result[key] = number // 愚行
  }
}
console.log(result) // { b: 2, d: 4, ... }
after
const isEven = num => num % 2 === 0 // 偶数かを判定する関数を用意
const result = _.pickBy(_.mapValue(obj, Number), isEven)

配列にmapメソッドがあると思いますが、LodashのmapValueはそれのオブジェクト版だと考えると話が早いです。pickByは先ほどでてきた配列のfilterのオブジェクト版です。
ちなみに、Lodashは以下のように使うとメソッドチェーンがはかどります

メソッドチェーン
_(obj).mapValue(Number).pickBy(isEven) // { b: 2, d: 4, ... }

さいごに

他にもletを使いたくなる場面があったらご指摘いただけますと幸いです。
また、記事に不備がございましたらご指摘いただけますと幸いです。

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

null、undefinedの判定

以下で判定可能

var nullOrUndefined = (val) => {
    // ==演算子を使用する。
    // ===にすると厳密な比較が行われ、undefinedの場合falseになる
    if (val == null) {
        console.log("null or undefined");
    } else {
        console.log(val);
    }
};

var a;
nullOrUndefined(a);
// "null or undefined"
var b = null;
nullOrUndefined(b);
// "null or undefined"
var c = "hoge";
nullOrUndefined(c);
// "hoge"

コーディング規約によっては==の使用が禁止されていて
===しか使用できないことがある(体験談…)

その場合の代替案をいくつか調べてみた。

1.自分でメソッドを作る

無ければ作ればいいじゃないの精神。

var isNullOrUndefined = (val) => {
    if (val === null) return true;
    if(val === undefined) return true;
    return false;
};

2.!hogeによる判定

0、false、空文字の場合もtrueと判定されることを理解して使用する。

if (!hoge) { 
    //null, undefined, 0, 空文字(''), falseの場合trueとなりこのブロックに入る
}

自分は判定対象の値に0が入っていることもあり1の方法を使用していたが、
2つ以上の値を判定する場合、記述数が多くなりモヤモヤしていた。

// もしも変数名が長いと手に負えない
if (isNullOrUndefined(a) && isNullOrUndefined(b)){}

2つ以上の値のチェックが頻繁に必要な場合、可変長で引数を受け取るメソッドを作成しておいても良いのかもしれない。

var isNullOrUndefinedAll = (...args) => {
  for (let val of args) {
    if (val !== null && val !== undefined) return false;
  }
  return true;
};
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript var,let,const 使い分け

はじめに

実装でjsを書いている際に、変数宣言(var,let,const)の使い分けが整理できていなかったのでこの機会にまとめておこうと思う。

var

再宣言・再代入が可能で、初期化なしの宣言が可能。

var x = 1;
console.log(x); //1

x = 2;
console.log(x); //2 再代入

var x = 3; //再宣言
console.log(x); //3

var s; // 初期化なしの宣言

スコープは関数スコープ
変数xは関数のスコープ内でのみ参照できる。

function (){
    var x = 0;
    console.log(x); //0
}

console.log(x); //error x is not defined

let

再代入は可能だが再宣言は不可となっている。初期化なしの宣言が可能。

let x = 1;
console.log(x); //1

x = 2;
console.log(x); //2 再代入

let x = 3; //SyntaxError 再宣言はできない

let s; // 初期化なしの宣言

スコープはブロックスコープ
ブロック内で宣言された変数は、スコープ内でのみ参照でき、スコープの外側からは参照できない。

{
    let x = 1;
    console.log(x); // => 1
}

console.log(x); // => ReferenceError: x is not defined
function fn(){
   let x = 1;
   console.log(x); 
{
   let x = 2;
   console.log(x); 
}
   console.log(x); 
}

fn()

結果はこんな感じ

// => 1
      2
      1

const

再宣言、再代入が不可で、更に初期化なしの宣言が不可。

const x = 1;
console.log(x); //1

const = 2;
console.log(x); //2 TypeError

const x = 3; //SyntaxError 再宣言はできない

const s; //SyntaxError

スコープはletと同じくブロックスコープ。

キーワードなしの宣言

var, let, constをつけなくても変数宣言は成立する。ただしグローバルに参照できる変数となるためバグの元となり非推奨。

変数の巻き上げ

JavaScript の変数に独特な点として、例外を発生させることなく後に宣言した変数を参照できる点がある。これを巻き上げという。
関数内で宣言したローカル変数をすべてundefinedで初期化するということ、その関数の先頭で宣言されたものと見なされるというもの。
例として

var x = 0;
function (){
    console.log(x);
    var x = 1;
    console.log(x);
}

// => undefined
      1

使い分け

varは使わず、let/constを使う。理由として

varは書き換えが可能なこと、巻き上げ時のバグを生み出しやすいことがあげられる。
letやconstを使っていれば、書き換えてしまってもエラーを出してくれるので、バグ発見に繋がりやすい。

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

PythonでEDINETコードリストを取得する

金融界隈で定量的な分析やデータサイエンスをやっている9uantです.
twitterもやってるので,興味ある方はぜひフォローしていただけると!

証券コードとEDINETコードを対応させる表データの取得が面倒だったため,共有したい.

EDINETコードと証券コード

証券コードは,上場企業の株価情報を取得する際に使用する4桁の数字である.実際には末尾に0を加えて5桁とする.
EDINETコードは,決算情報のデータベースであるEDINET上で企業情報を取得する際に使用するアルファベット+数字である.

4桁の証券コードをEDINETコードに変換する方法を紹介する.

EDINETコードリストを取得する

EDINETタクソミノ及びコードリストの下部に,「EDINETコードリスト」がある.今回はPythonを使ってこのcsvデータを取得する.

chromedriverを事前にダウンロードする必要がある.
chromedriverのダウンロードサイトから,ご自身のchromeのバージョンに合わせてダウンロードされたい.

get_edinet_code_csv.py
import glob
import os
import shutil
import time
from selenium import webdriver
import zipfile

def get_edinet_code_csv(edinetcode_dir):
    '''
    EDINETコードリストのcsvファイルを指定したディレクトリにダウンロードする
    Prameter:
        edinetcode_dir: str
            EDINETコードリストのcsvファイルをダウンロードするディレクトリ

    Return:
        edinet_code_list_path: str
            EDINETコードリストのcsvファイルが存在するパス
    '''

    '''
    # ディレクトリが既に存在する場合は,ディレクトリを削除する
    if os.path.exists(edinetcode_dir):
        shutil.rmtree(edinetcode_dir)
    '''

    # seleniumでchromeからzipファイルをダウンロード
    chromeOptions = webdriver.ChromeOptions()
    prefs = {"download.default_directory" : edinetcode_dir} # 保存先のディレクトリの指定
    chromeOptions.add_experimental_option("prefs",prefs)
    chromeOptions.add_argument('--headless') # ブラウザ非表示

    driver = webdriver.Chrome('chromedriverのパス', chrome_options=chromeOptions)
    # EDINETのEDINETコードリストにアクセス
    driver.get('https://disclosure.edinet-fsa.go.jp/E01EW/BLMainController.jsp?uji.bean=ee.bean.W1E62071.EEW1E62071Bean&uji.verb=W1E62071InitDisplay&TID=W1E62071&PID=W0EZ0001&SESSIONKEY=&lgKbn=2&dflg=0&iflg=0')
    driver.execute_script("EEW1E62071EdinetCodeListDownloadAction('lgKbn=2&dflg=0&iflg=0&dispKbn=1');")
    time.sleep(5)
    driver.quit()

    # ダウンロードしたzipファイルのパスを取得
    list_of_files = glob.glob(edinetcode_dir+r'/*') # ワイルドカードを追加
    latest_file = max(list_of_files, key=os.path.getctime) #作成日時が最新のファイルパスを取得

    # zipファイルを同じディレクトリに展開
    zip_f = zipfile.ZipFile(latest_file)
    zip_f.extractall(edinetcode_dir)
    zip_f.close()

    # zipファイルを削除
    os.remove(latest_file)

    list_of_files = glob.glob(edinetcode_dir+r'/*') # ワイルドカードを追加
    return max(list_of_files, key=os.path.getctime) # 展開したcsvファイルのパスを返す

証券コード→EDINETコード

証券コードの配列を,EDINETコードの配列に変換する.

stockcode_to_edinetcode.py
import numpy as np
import pandas as pd

edinet_code_path=get_edinet_code_csv(r"EDINETコードリストのcsvのダウンロード先ディレクトリのパス")

edinet_code_df=pd.read_csv(edinet_code_path,encoding="cp932",header=1,usecols=['EDINETコード', '提出者名', '証券コード'])

def stockcode_to_edinetcode(codes):
    '''
    証券コード(の配列)に対応するEDINETコードの配列を取得する
    Parameter:
        codes: int or float or str or list
            証券コード,またはその配列

    Return:
        edinet_codes: list
            引数の順番に対応したEDINETコードの配列
    '''

    # 全ての引数を配列に変換
    if type(codes) in (str, int, float):
        codes = [int(codes)]

    edinet_codes = []

    for code in codes:
        # 4桁の証券コードを5桁に変換
        if len(str(int(code)))==4:
            code = str(int(code))+'0'

        tmp = edinet_code_df[edinet_code_df['証券コード']==int(code)]['EDINETコード']

        if len(tmp)==0: # 対応するEDINETコードが存在しない場合np.nanを返す
            edinet_codes.append(np.nan)
        else:
            edinet_codes.append(tmp.to_list()[0])

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

今までJW Playerを使っていたけどplyr.jsをカスタマイズしてみた(3)

前回まで

前回まで でボタンの色や配置をカスタマイズすることはできました。
ここからは独自のボタンを追加するなどさらなるカスタマイズに進みます。

オリジナルのボタン追加

下記の公式情報のように、HTMLをJavaScriptにガシガシ書くことで追加できます。

公式の情報
const controls = `
<div class="plyr__controls">
    <!-- もう一度再生ボタン -->
    <button type="button" class="plyr__control" data-plyr="restart">
        <svg role="presentation"><use xlink:href="#plyr-restart"></use></svg>
        <span class="plyr__tooltip" role="tooltip">Restart</span>
    </button>
    <!-- 10秒戻るボタン -->
    <button type="button" class="plyr__control" data-plyr="rewind">
        <svg role="presentation"><use xlink:href="#plyr-rewind"></use></svg>
        <span class="plyr__tooltip" role="tooltip">Rewind {seektime} secs</span>
    </button>
    <!-- 再生、停止ボタン -->
    <button type="button" class="plyr__control" aria-label="Play, {title}" data-plyr="play">
        <svg class="icon--pressed" role="presentation"><use xlink:href="#plyr-pause"></use></svg>
        <svg class="icon--not-pressed" role="presentation"><use xlink:href="#plyr-play"></use></svg>
        <span class="label--pressed plyr__tooltip" role="tooltip">Pause</span>
        <span class="label--not-pressed plyr__tooltip" role="tooltip">Play</span>
    </button>
    <!-- 10秒進むボタン -->
    <button type="button" class="plyr__control" data-plyr="fast-forward">
        <svg role="presentation"><use xlink:href="#plyr-fast-forward"></use></svg>
        <span class="plyr__tooltip" role="tooltip">Forward {seektime} secs</span>
    </button>
    <!-- プログレスバー -->
    <div class="plyr__progress">
        <input data-plyr="seek" type="range" min="0" max="100" step="0.01" value="0" aria-label="Seek">
        <progress class="plyr__progress__buffer" min="0" max="100" value="0">% buffered</progress>
        <span role="tooltip" class="plyr__tooltip">00:00</span>
    </div>
    <!-- 現在時間 -->
    <div class="plyr__time plyr__time--current" aria-label="Current time">00:00</div>
    <!-- 全体の再生時間 -->
    <div class="plyr__time plyr__time--duration" aria-label="Duration">00:00</div>
    <!-- ミュート、ミュート解除ボタン -->
    <button type="button" class="plyr__control" aria-label="Mute" data-plyr="mute">
        <svg class="icon--pressed" role="presentation"><use xlink:href="#plyr-muted"></use></svg>
        <svg class="icon--not-pressed" role="presentation"><use xlink:href="#plyr-volume"></use></svg>
        <span class="label--pressed plyr__tooltip" role="tooltip">Unmute</span>
        <span class="label--not-pressed plyr__tooltip" role="tooltip">Mute</span>
    </button>
    <!-- ボリューム -->
    <div class="plyr__volume">
        <input data-plyr="volume" type="range" min="0" max="1" step="0.05" value="1" autocomplete="off" aria-label="Volume">
    </div>
    <!-- 字幕表示/非表示ボタン -->
    <button type="button" class="plyr__control" data-plyr="captions">
        <svg class="icon--pressed" role="presentation"><use xlink:href="#plyr-captions-on"></use></svg>
        <svg class="icon--not-pressed" role="presentation"><use xlink:href="#plyr-captions-off"></use></svg>
        <span class="label--pressed plyr__tooltip" role="tooltip">Disable captions</span>
        <span class="label--not-pressed plyr__tooltip" role="tooltip">Enable captions</span>
    </button>
    <!-- フルスクリーンボタン -->
    <button type="button" class="plyr__control" data-plyr="fullscreen">
        <svg class="icon--pressed" role="presentation"><use xlink:href="#plyr-exit-fullscreen"></use></svg>
        <svg class="icon--not-pressed" role="presentation"><use xlink:href="#plyr-enter-fullscreen"></use></svg>
        <span class="label--pressed plyr__tooltip" role="tooltip">Exit fullscreen</span>
        <span class="label--not-pressed plyr__tooltip" role="tooltip">Enter fullscreen</span>
    </button>
</div>
`;

// 「controls:」はいらない
const player = new Plyr('#player', { controls });

実はこの方法

2つ制約があり

  • ピクチャーインピクチャとAirPlayは追加できるが、ブラウザが対応しているのかは自分で実装する必要がある。
  • 設定メニューは非対応

そうなのです。画質変更、再生速度変更などの設定メニューはこの方法では自分で実装するしかないのです。
再生速度は「plaer.speed=2」のように瞬殺だったのですが、画質変更がえげつなく難しかったです。
ただ、出来てしまえば「まあ、当然ですよね。」という内容でした。

myplayer.js
// 画質ごとの動画ファイルのパスを格納する変数
var video_url 1080 = "video/file_1080p.mp4";
var video_url 720 = "video/file_720p.mp4";
var video_url 480 = "video/file_480p.mp4";

$(function() {
  const controls = `
<div class="plyr__controls">
    <!-- もう一度再生ボタン -->
    <button type="button" class="plyr__control" data-plyr="restart">
        <svg role="presentation"><use xlink:href="#plyr-restart"></use></svg>
        <span class="plyr__tooltip" role="tooltip">Restart</span>
    </button>
    <!-- 10秒戻るボタン -->
    <button type="button" class="plyr__control" data-plyr="rewind">
        <svg role="presentation"><use xlink:href="#plyr-rewind"></use></svg>
        <span class="plyr__tooltip" role="tooltip">Rewind {seektime} secs</span>
    </button>
    <!-- 再生、停止ボタン -->
    <button type="button" class="plyr__control" aria-label="Play, {title}" data-plyr="play">
        <svg class="icon--pressed" role="presentation"><use xlink:href="#plyr-pause"></use></svg>
        <svg class="icon--not-pressed" role="presentation"><use xlink:href="#plyr-play"></use></svg>
        <span class="label--pressed plyr__tooltip" role="tooltip">Pause</span>
        <span class="label--not-pressed plyr__tooltip" role="tooltip">Play</span>
    </button>
    <!-- 10秒進むボタン -->
    <button type="button" class="plyr__control" data-plyr="fast-forward">
        <svg role="presentation"><use xlink:href="#plyr-fast-forward"></use></svg>
        <span class="plyr__tooltip" role="tooltip">Forward {seektime} secs</span>
    </button>
    <!-- プログレスバー -->
    <div class="plyr__progress">
        <input data-plyr="seek" type="range" min="0" max="100" step="0.01" value="0" aria-label="Seek">
        <progress class="plyr__progress__buffer" min="0" max="100" value="0">% buffered</progress>
        <span role="tooltip" class="plyr__tooltip">00:00</span>
    </div>
    <!-- 現在時間 -->
    <div class="plyr__time plyr__time--current" aria-label="Current time">00:00</div>
    <!-- 全体の再生時間 -->
    <div class="plyr__time plyr__time--duration" aria-label="Duration">00:00</div>
    <!-- ミュート、ミュート解除ボタン -->
    <button type="button" class="plyr__control" aria-label="Mute" data-plyr="mute">
        <svg class="icon--pressed" role="presentation"><use xlink:href="#plyr-muted"></use></svg>
        <svg class="icon--not-pressed" role="presentation"><use xlink:href="#plyr-volume"></use></svg>
        <span class="label--pressed plyr__tooltip" role="tooltip">Unmute</span>
        <span class="label--not-pressed plyr__tooltip" role="tooltip">Mute</span>
    </button>
    <!-- ボリューム -->
    <div class="plyr__volume">
        <input data-plyr="volume" type="range" min="0" max="1" step="0.05" value="1" autocomplete="off" aria-label="Volume">
    </div>
    <!-- 字幕表示/非表示ボタン -->
    <button type="button" class="plyr__control" data-plyr="captions">
        <svg class="icon--pressed" role="presentation"><use xlink:href="#plyr-captions-on"></use></svg>
        <svg class="icon--not-pressed" role="presentation"><use xlink:href="#plyr-captions-off"></use></svg>
        <span class="label--pressed plyr__tooltip" role="tooltip">Disable captions</span>
        <span class="label--not-pressed plyr__tooltip" role="tooltip">Enable captions</span>
    </button>
    <!-- フルスクリーンボタン -->
    <button type="button" class="plyr__control" data-plyr="fullscreen">
        <svg class="icon--pressed" role="presentation"><use xlink:href="#plyr-exit-fullscreen"></use></svg>
        <svg class="icon--not-pressed" role="presentation"><use xlink:href="#plyr-enter-fullscreen"></use></svg>
        <span class="label--pressed plyr__tooltip" role="tooltip">Exit fullscreen</span>
        <span class="label--not-pressed plyr__tooltip" role="tooltip">Enter fullscreen</span>
    </button>
    <!-- 画質選択ボタン -->
    <button type="button" class="plyr__control" data-plyr="settings" id="plyr_quality">
        <span class="plyr__label">480</span>
        <span class="plyr__tooltip" role="tooltip">画質選択</span>
    </button>
    <div id="quality_menu" class="hidden pa tc">
      <div val="1080" class="quality_select cp">1080p</div>
      <div val="720" class="quality_select cp">720p</div>
      <div val="480" class="quality_select cp">480p</div>
    </div>
</div>
`;

  // 「controls:」はいらない
  const player = new Plyr('#player', { controls });

  // 画質ごとのファイルをsourceに設定
  player.source = {
      type: "video",
      sources: [
        { src: video_url_480, type: "video/mp4", size: 480 },
        { src: video_url_720, type: "video/mp4", size: 720 },
        { src: video_url, type: "video/mp4", size: 1080 }
      ],
      poster: overview_url
  };

  // 最初は画質リストは非表示
  $("#quality_menu").hide();

  // 画質選択ボタンの動作を追加
  $("#plyr_quality").click(function() {
    // 画質リストを表示
    $("#quality_menu").toggle();
  });

  // 画質リストから選択した時の動作
  $(".quality_select").click(function() {
    // 経過時間を取得
    cur_time = player.currentTime;
    setPlayerSource(player, $(this).attr("val"));
    // 0秒以上なら再生されているとみなして、同じ時間から再生
    if (cur_time > 0) {
      player.play();
      player.currentTime = Number(cur_time);
    }
    $("#quality_menu").hide();
  });
});

function setPlayerSource(player, cur_val) {
  if (cur_val == "1080") {
    player.media.src = video_url;
  } else if (cur_val == "720") {
    player.media.src = video_url_720;
  } else {
    player.media.src = video_url_480;
  }
}

そうです。要するにやっていることは「video要素のsrc属性を変える」だけです。
ただ、私の英語力の問題か、公式の文書から「playerのmediaプロパティにvideo要素が入っている」ということが読み解けなかったので、単純にコンソールで色々いじって見つけました。
なので、公式推奨のやり方ではないです。

そうするとこのように表示されます。
スクリーンショット 2020-08-02 10.20.58.png
見た目がものすごいイマイチです...。

myplayer.js
// ... 略

    <div class="plyr__progress" style="width:100%;">
        <input data-plyr="seek" type="range" min="0" max="100" step="0.01" value="0" aria-label="Seek">
        <progress class="plyr__progress__buffer" min="0" max="100" value="0">% buffered</progress>
        <span role="tooltip" class="plyr__tooltip">00:00</span>
    </div>

// ... 略

    <div id="quality_menu" style="position: absolute; top: -50px; background: red;">
      <div val="1080" class="quality_select cp">1080p</div>
      <div val="720" class="quality_select cp">720p</div>
      <div val="480" class="quality_select cp">480p</div>
    </div>

とりあえず上記2箇所だけ修正すればプレイヤーっぽくなって、画質メニューを開いても幅がずれたりはしなくなります。
スクリーンショット 2020-08-02 10.29.35.png

参考リンク:
公式のコントローラーの説明
https://github.com/sampotts/plyr/blob/master/CONTROLS.md

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

カリー化チートシート

拙作『不動点コンビネータを用いた無名再帰関数の実行まとめ』の補足説明として書き始めたところ,カリー化関数を記述・利用するための独立したチートシートとした方が少なくとも約1名(自分自身)には役立ちそうだったので,新しく記事にした.なお,カリー化してくれる関数の定義ではないことに注意.複数言語にわたっている都合上,各言語に精通している方々のツッコミ歓迎.

記法のみの一覧は次の通り.

言語 各引数の戻り値 各引数の指定 備考
Haskell \->による無名関数 (・・・(f a) a)・・・a 自動的にカリー化
Scheme lambdaによる無名関数 (・・・((f a) a)・・・a)
Python 関数内部で定義した関数 f(a)(a)・・・(a) 無名関数も使用可
Ruby ->を用いた無名関数 f.(a).(a)・・・.(a)
JavaScript =>functionを用いた無名関数 f(a)(a)・・・(a)
Scala =>を用いた無名関数 f(a)(a)・・・(a) カリー化関数あり,強い型付け
Perl subを用いた無名サブルーチン f(a)->(a)・・・->(a) $f->(a)・・・の場合あり
Go言語 funcを用いた無名関数 f(a)(a)・・・(a) 強い型付け
PHP functionuseを用いた無名関数 f(a)(a)・・・(a)

Haskell(GHC)

Haskellでは,複数引数で定義しても自動的にカリー化される.カリー化関数の引数指定は(・・・(関数 引数) 引数)・・・引数である.

Prelude> func x y z = if x > 0 then y else z
Prelude> func (-100) 0 (-1)
-1
Prelude> ((func (-100)) 0) (-1)
-1

\および->を用いた無名関数を戻り値にして実現する方法は次の通り.ただし,この場合でも引数の複数指定が可能.

Prelude> func = \x -> \y -> \z -> if x > 0 then y else z
Prelude> ((func (-100)) 0) (-1)
-1
Prelude> func (-100) 0 (-1)
-1
Prelude> func = \x y z -> if x > 0 then y else z
Prelude> ((func (-100)) 0) (-1)
-1
Prelude> func (-100) 0 (-1)
-1

Scheme(Gauche)

lambdaを用いた無名関数を戻り値にして実現する.カリー化関数の引数指定は(・・・((関数 引数) 引数)・・・引数)である.

gosh> (define (func x y z) (if (> x 0) y z))
func
gosh> (func -100 0 -1)
-1
gosh> (define func (lambda (x) (lambda (y) (lambda (z) (if (> x 0) y z)))))
func
gosh> (((func -100) 0) -1)
-1

Python(Python3,Python2)

lambdaを用いた無名関数を用いることもできるが,無名関数を直接変数に代入するのがPEP8非推奨ということもあり,関数内部で定義した関数を戻り値にする方法が一般的である.カリー化関数の引数指定は関数(引数)(引数)・・・(引数)である.

>>> def func(x, y, z): return y if x > 0 else z
... 
>>> func(-100, 0, -1)
-1
>>> def func(x):
...     def func(y):
...         def func(z): return y if x > 0 else z
...         return func
...     return func
... 
>>> func(-100)(0)(-1)
-1
>>> func = lambda x: lambda y: lambda z: y if x > 0 else z    # PEP8非推奨
>>> func(-100)(0)(-1)
-1

Ruby(CRuby,JRuby)

->を用いた無名関数を戻り値にして実現する.カリー化関数の引数指定は関数.(引数).(引数)・・・.(引数)である.

def func(x,y,z) return x > 0 ? y : z end
p func(-100,0,-1)         # => -1

func = -> x { -> y { -> z { return x > 0 ? y : z } } }
p func.(-100).(0).(-1)    # => -1

JavaScript(Node.js)

=>functionを用いた無名関数を戻り値にする方法で実現する.カリー化関数の引数指定は関数(引数)(引数)・・・(引数)である.

function func1(x,y,z) { return x > 0 ? y : z }
console.log(func1(-100,0,-1))      // => -1

func2 = x => y => z => x > 0 ? y : z
console.log(func2(-100)(0)(-1))    // => -1

function func3(x) {
  return function (y) {
    return function (z) {
      return x > 0 ? y : z
    }
  }
}
console.log(func3(-100)(0)(-1))    // => -1

Scala(Scala 2.11 + Java VM 12)

Scalaでは,カリー化するメソッドcurriedが用意されている.ただし,複数引数の無名関数を=>によって一度定義してからcurriedを適用する.カリー化関数の引数指定は関数(引数)(引数)・・・(引数)である.

scala> def func(x: Int, y: Int, z: Int): Int = if (x > 0) y else z
func: (x: Int, y: Int, z: Int)Int

scala> func(-100,0,-1)
res0: Int = -1

scala> val func = (x: Int, y: Int, z: Int) => if (x > 0) y else z
func: (Int, Int, Int) => Int = <function3>

scala> val func_curried = func.curried
func_curried: Int => (Int => (Int => Int)) = <function1>

scala> func_curried(-100)(0)(-1)
res1: Int = -1

=>のみを用いた無名関数によって実現する方法は次の通り.強い型付け言語であるため,関数全体の型の推移を明示する必要がある.

scala> val func: Int => (Int => (Int => Int)) = (x: Int) => (y: Int) => (z: Int) => if (x > 0) y else z
func: Int => (Int => (Int => Int)) = <function1>

scala> func(-100)(0)(-1)
res2: Int = -1

Perl(perl 5)

subを用いた無名関数(サブルーチン)を戻り値にして実現する.カリー化関数の引数指定は関数(引数)->(引数)・・・->(引数)である.なお,関数本体の名前も無名関数とした場合は$関数->(引数)->(引数)・・・->(引数)である.

sub func { my ($x,$y,$z) = @_; $x > 0 ? $y : $z; };
print func(-100,0,-1), "\n";    # => -1

sub func_curried { my $x = shift; return sub { my $y = shift; return sub { my $z = shift; return $x > 0 ? $y : $z; }; }; };
print func_curried(-100)->(0)->(-1), "\n";    # => -1

my $func_curried2 = sub { my $x = shift; return sub { my $y = shift; return sub { my $z = shift; return $x > 0 ? $y : $z; }; }; };
print $func_curried2->(-100)->(0)->(-1), "\n";    # => -1

Go言語(gc)

funcを用いた無名関数を戻り値にして実現する.カリー化関数の引数指定は関数(引数)(引数)・・・(引数)である.なお,強い型付け言語であるため,扱う引数が増えるほど,各引数の関数の戻り値に対する型付け記述が増えていく.

package main
import "fmt"

func func1 (x, y, z int) int { if x > 0 { return y } else { return z } }
func func2 (x int) func(int) func(int) int {
    return func(y int) func(int) int {
        return func(z int) int {
            if x > 0 { return y } else { return z }
        }
    }
}

func main() {
    fmt.Println(func1(-100,0,-1))      // => -1
    fmt.Println(func2(-100)(0)(-1))    // => -1
}

PHP(PHP 7)

functionuseを用いた無名関数を戻り値にする方法で実現する.カリー化関数の引数指定は関数(引数)(引数)・・・(引数)である.

<?php

function func1($x,$y,$z) {
    return ($x > 0) ? $y : $z;
}
echo func1(-100,0,-1) . PHP_EOL;
// => -1

function func2($x) {
    return function($y) use ($x) {
        return function($z) use ($x,$y) {
            return ($x > 0) ? $y : $z;
        };
    };
}
echo func2(-100)(0)(-1) . PHP_EOL;
// => -1

備考

カリー化の概要

カリー化とは,高階関数の機能を利用して,複数の引数を指定する関数を,ひとつの引数のみを指定する関数の繰り返しに変換することである.処理記述の共有が可能な『部分適用』の手段として有名であるが,あくまで利用例のひとつである.カリー化自体の特徴は,ラムダ計算などの数学的な理論を適用しやすいことに加え,引数ごとに値の適用を調節したり,データ構造を要素ごとに受け取ったりすることで,より簡潔で柔軟なプログラミングが可能となることである.

なお,下記のPythonの例のように,今回の記述方法を用いることで,複数引数をもつ既存の関数のカリー化も容易に行える.ただし,複数引数をもつ既存の関数をカリー化関数に変換してくれる関数やマクロは作成できない.理由は,既存関数の引数の数が不定であり,任意の数の無名関数や内部関数を(クロージャ機能を含めて)生成できないためである.Scalaのcurriedは個別に型定義された複数引数をもつ無名関数から変換しており,Haskellは処理系がカリー化関数のみを扱っている(関数定義が行われ引数の数や型を解析する時点でカリー化を行っている)

Pythonでのカリー化関数利用例

>>> def is_t(t):
...     def r(v): return isinstance(v, t)
...     return r
... 
>>> T = 10, "hoge", 20.4, False, "hage"
>>> tuple(map(is_t(str), T))
(False, True, False, False, True)
>>> tuple(map(is_t(int), T))
(True, False, False, True, False)
>>> def func(y):
...     def func(z):
...         def func(w):
...             def func(x): return y if x > 0 else z if x < 0 else w
...             return func
...         return func
...     return func
... 
>>> func1 = func('positive')('negative')
>>> func2 = func1('zero')
>>> func2(1)
'positive'
>>> func2(-1)
'negative'
>>> func2(0)
'zero'
>>> func2 = func1('ゼロ')
>>> func2(0)
'ゼロ'
>>> T = (3, -2, 0, 1, -7)
>>> dict(zip(T, map(func2, T)))
{3: 'positive', -2: 'negative', 0: 'ゼロ', 1: 'positive', -7: 'negative'}
>>> def recur(f, t): return f if not t else recur(f(t[0]), t[1:])
... 
>>> dict(zip(T, map(recur(func, ('正', '負', 'ゼロ')), T)))
{3: '正', -2: '負', 0: 'ゼロ', 1: '正', -7: '負'}

(同様の内容をSchemeで記述したものはこちら

変更履歴

  • 2020-08-02:PHPを追加
  • 2020-08-02:記法のみの一覧を追加
  • 2020-08-02:Go言語を追加
  • 2020-08-02:初版公開(Haskell,Scheme,Python,Ruby,JavaScript,Scala,Perl,Python利用例)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

複数の要素でスクロール検知してアニメーションを行う

IntersectionObserver

".made-item"が付いている要素を取得して、それぞれの要素が特定のスクロールポイントに来たときにclassを付与、消去するプログラムです。

function() {
      const items = document.querySelectorAll('.made-item')
      const cb = function(entries, observer) {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            console.log('はいったときの処理');
            console.log(entry.target);
            entry.target.classList.add('inview')
          } else {
            console.log('でたときの処理');
            entry.target.classList.remove('inview')
          }
        })
      }
      const options = {
        root: null,
         //交差する要素を設定できる 変更することはあまりない
        rootMargin: '0px 0px -300px 0px',
        //交差点の場所をmarginのように指定できる
        threshold: 0
        // 初期値は0 0は交差点に入ってくる要素の下の部分 1は上の部分でcbが呼ばれる
        // 配列で[0, 0.5, 1とすると要素の上、真ん中、下でそれぞれcbが呼ばれる
      }
      const io = new IntersectionObserver(cb, options)
      items.forEach(item => {
        io.observe(item)
     // io.observe(item2)
     // io.observe(item3)
     // ioの中に↑こんな感じで要素を複数入れられます
      })
    }
  • querySelictorAllで複数のclassを指定
  • forEachでそれを回して変数io(newしたIntersectionObserver)に代入
  • 上記の要素がcbの引数entriesに入るので入ったとき、出たときの処理を記入
  • 交差点の要素はentry.targetで確認できる

scrollY

let scroll = 0
function() {
    scroll = window.scrollY
    console.log(scroll);
    }
  • スクロールで上端からのピクセル数を検知
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

複数の要素をスクロール検知してアニメーションを行う

よくポートフォリオサイトなどで見かけるスクロールすると画像がフェードインしてくる感じのものが作れます。

IntersectionObserver

".made-item"が付いている要素を取得して、それぞれの要素が特定のスクロールポイントに来たときにclassを付与、消去するプログラムです。

function() {
      const items = document.querySelectorAll('.made-item')
      const cb = function(entries, observer) {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            console.log('はいったときの処理');
            console.log(entry.target);
            entry.target.classList.add('inview')
          } else {
            console.log('でたときの処理');
            entry.target.classList.remove('inview')
          }
        })
      }
      const options = {
        root: null,
         //交差する要素を設定できる 変更することはあまりない
        rootMargin: '0px 0px -300px 0px',
        //交差点の場所をmarginのように指定できる
        threshold: 0
        // 初期値は0 0は交差点に入ってくる要素の下の部分 1は上の部分でcbが呼ばれる
        // 配列で[0, 0.5, 1とすると要素の上、真ん中、下でそれぞれcbが呼ばれる
      }
      const io = new IntersectionObserver(cb, options)
      items.forEach(item => {
        io.observe(item)
     // io.observe(item2)
     // io.observe(item3)
     // ioの中に↑こんな感じで要素を複数入れられます
      })
    }
  • querySelictorAllで複数のclassを指定
  • forEachでそれを回して変数io(newしたIntersectionObserver)に代入
  • 上記の要素がcbの引数entriesに入るので入ったとき、出たときの処理を記入
  • 交差点の要素はentry.targetで確認できる

scrollY

let scroll = 0
function() {
    scroll = window.scrollY
    console.log(scroll);
    }
  • スクロールで上端からのピクセル数を検知
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

非同期の双方向更新を実現するためのテクニック

近年、Fluxのように双方向の更新を極力避けて、単方向の更新に還元させようという流れがあります。ただ、どうしても双方向の更新が避けられない場合はあるでしょう。Goole Docsのように、複数の人が同じドキュメントを編集する場合。画面上の複数の入力コンポーネントが相互に同期しているような場合です。

特定の言語に依存しない話ですが、Javascriptで使うことが多いとは思われるため、タグにJavascriptを入れています。

1. 一番単純な実装

まず、一番シンプルに、Aが更新されたらBに反映させる、Bが更新されたらAに反映させる、という実装を考えます。

2. 外部から更新を受け入れた場合は、更新の報告をしない

この際、単純に実装するとA→B→A→Bという無限ループになってしまいます。これを解決するためには以下のような方法が考えられます。

それは、「外部から更新を受け入れた場合は、更新の報告をしない」ことです。

3. サーバ側でupdateIdを管理し、最後のコミットを反映させる

ただ、これだと、AとBが同時に更新した場合、最終的に、どちらのサーバ反映が早かったかに関わらず、AではBが反映され、BではAが反映されることになります。つまり、AとBの現在状態がずれてしまいます。

これに対応するためには、サーバ側で、更新の受付を管理し、最新の状態を確定させる必要があります。

これは、socket.ioを使う場合、フレームワーク側で更新順序の管理するので、サーバ側で特別な対応は不要です。

この場合、クライアント側では、自分が行った更新がサーバを通して通知されてきた時に無視せずに、受け入れるような実装が必要です。自分が行った更新がサーバを通して通知されてきた時に無視するような実装にすると、更新がAとBで同時に行われて、Aの方が先にサーバに到達した時、本来、現在状態はBの更新でなければいけないのに、Bの側ではそれを受け取れず、現在状態がずれてしまいます。

4. 更新中の通知の無視

しかし、これにはまた問題があります。
自分が行った更新がサーバを通して通知されてきた時に受け入れるとなると、
そのラウンドトリップの間に行われた変更が一時的にであれ破棄されることになってしまいます。
これは、ユーザから見ると、行った変更が一時的に巻き戻されるように見えることになり、
受け入れられるものではありません。

これを解決するための方法は、

「自分が更新を行ってそれが通知されるまでの間、更新の受け入れを先延ばしする」

ことです。

5. 更新中の自己更新の無視

しかし、これにもまた問題があります。それは、外部からの更新の受け入れもストップしてしまうため、AとBがずっと更新し続けると、永遠にAとBの状態がずれたままになることです。

これを解決するには、上のルールを以下のように修正すれば良いことになります。

「現在状態が、自己の更新の結果によるものである場合、自分が行った更新の通知は無視する」

つまり、自分が更新をしている間でも、他の人の更新は受け入れます。そして、いったん他の人の更新を受け入れれば、その後に到着した自分が行った更新は受け入れます。

これで初めて、現在状態のズレがなくなり完全な同期が実現することになります。

6. まとめ

こうやって見ると、双方向更新を実現するため考慮しないといけないことが想像以上に多いことが分かると思います。

ここから分かる重要なことは、双方向更新が必要になったとき、一番最初に考えないといけないことは「双方向更新をしないで良いように設計できないか検討すること」です。それがどうしても無理だった場合、この記事のことを思い出してもらえると幸いです。

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

極限まで単純化したPromiseのサブセット

例外処理等はなく、とにかくthenableにすることだけに特化したPromiseのサブセットです。あまり実用的な意味はありませんが、Promiseの内部の実装を理解する上で参考になればと思います。

const Promise = window.Promise || function Promise(executor) {
  const callbacks = [];
  let isResolved = false;
  let resolvedValue = null;
  const resolved = (value) => {
    isResolved = true;
    resolvedValue = value;
    callbacks.forEach(callback => callback(resolvedValue))
  }
  const rejected = (err) => {
    callbacks.splice(0);
    console.error(err);
  }
  executor(resolved, rejected);
  this.then = (callback) => {
    if (isResolved) {
      callback(value);
    } else {
      callbacks.push(callback);
    }
  };
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SQL文で変数を使う方法 in JavaScript

はじめに

Node.jsでExpress+Mysqlを用いてRestApiを作成する際、postでの処理に詰まったため備忘録として記事を書きます。

問題

id(int) name(String)
1 test

このようなテーブルに対して、以下のソースを用いてpostでレコードを挿入する際、数値は入るが文字列はエラーが出て入れることができなかった。

sample.js
id=req.body.id; 
name=req.body.name"; 

//jsonで{id:2,name:”sample”}というデータを送信

sql="INSERT INTO api_tbl VALUES("+id+","+name+")";
connection.query( sql,function (error, results, fields) {
   if (error) throw error;
   res.send("ok");
});

原因としては、postリクエストを送った際、sql文をセットしている変数sqlに以下のような文字列が格納されていたからと考えた。

sql="INSERT INTO api_tbl VALUES(2,sample)"

ここからnameの文字列を「""」で囲うために、変数nameの値を以下のように書き換えることで実装できた。

name= '"'+req.body.name+'"'

おわりに

SQL文はあまり学習していないので変なところで詰まってしまった…反省。
基本的な構文はすらすら書けるようしっかり学習します。
今回の手法、もっと簡単にできるやり方があったら教えてください!

(2020/8/2 追記)
@nagtkk さんのコメントで、プレースホルダ(?)をつかってsql文に値をセットするやり方を教えてもらいました!
実際に試してみたらしっかりと動作し、またソースも見やすく簡単に実装できたためこちらの手法を推奨します。

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

Javascript で天ざるそばをつくる

天ざるそばを作るとき、天ぷらを揚げてから、そばを茹でたら、天ぷらは冷えてしまう。そばを茹でてから天ぷらを揚げたら、そばにコシがなくなってしまう。できれば同時に作って、コシのあるそばでカラッとした天ぷらを食べたい。そんなわけでJavascriptでうまい天ざるそばを作る様子をシミュレートしてみよう。

ここで、天ざるそばのレシピを振り返ってみる。

A 天ぷらをつくる
  (1) 素材準備 (5秒)
  (2) 揚げる (10秒)

B そばを茹でる
  (1) はかる (2秒)
  (2) ゆでる (7秒)
  (3) あらう (3秒)

C A,Bをもりつける (5秒)

手順としては、A->B->Cではなく、 (A|B)->C でなければ、いけない。
カッコ内は、仮の調理時間。
念のために、絵で確認しておこう。
image.png

準備

このシミュレーションでは、揚げたり、ゆでたりするそれぞれの作業を始めたときに「〜開始」、その作業が終わったときに「〜完了」と報告するようにしたい。
料理作業を次のように定義することで、要件を満たす。
まずはじめに、天ぷらをつくるA-(1)を定義する。

A-(1)
function prepare(resolve, cooking_time){
  console.log("てんぷら素材準備開始");
  setTimeout(()=>resolve("てんぷら素材準備完了+cooking_time"),cooking_time*1000)
}
function prepare_tempra(sec){return new Promise((resolve)=>{prepare(resolve,sec)});}
prepare_tempra(5).then(r=>console.log(r))

これを実行すれば、次のように出力される。

"てんぷら素材準備開始"  <- 実行直後に表示される
"てんぷら素材準備完了5"  <- 5秒後に表示される

次に、天ぷらをつくるA-(2)を定義する。

A-(2)
function fry(resolve, cooking_time){
  console.log("てんぷら揚げ開始");
  setTimeout(()=>resolve("てんぷら完了"+cooking_time),cooking_time*1000)
}
function fry_tempra(sec){return new Promise((resolve)=>{fry(resolve,sec)});}
fry_tempra(10).then(r=>console.log(r))

これを実行すれば、次のように出力される。

"てんぷら揚げ開始"  <- 実行直後に表示される
"てんぷら完了10"  <- 10秒後に表示される

これらを順番に実行すれば、天ぷらが完成する。

A
const tempra = async () => {
  await prepare_tempra(5).then(r=>console.log(r))
  await fry_tempra(10).then(r=>console.log(r))
}

これを実行すると、次のように出力される。

"てんぷら素材準備開始"  <- 実行直後に表示される
"てんぷら素材準備完了5"  <- 5秒後に表示される
"てんぷら揚げ開始"  
"てんぷら完了10"  <- 15秒後に表示される

ひとまず、天ぷら手順は、OKだ。
同様にして、そば手順を定義していく。

B
function measure(resolve, cooking_time){
  console.log("そばをはかる");
  setTimeout(()=>resolve("そば計量完了"+cooking_time),cooking_time*1000)
}

function boil(resolve, cooking_time){
  console.log("そばをゆでる");
  setTimeout(()=>resolve("そばゆで完了"+cooking_time),cooking_time*1000)
}

function wash(resolve, cooking_time){
  console.log("そばをあらう");
  setTimeout(()=>resolve("そばあらい完了"+cooking_time),cooking_time*1000)
}

function measure_soba(sec){return new Promise((resolve)=>{measure(resolve,sec)});}
function boil_soba(sec){return new Promise((resolve)=>{boil(resolve,sec)});}
function wash_soba(sec){return new Promise((resolve)=>{wash(resolve,sec)});}

const soba = async () => {
  await measure_soba(2).then(r=>console.log(r)) 
  await boil_soba(7) .then(r=>console.log(r))
  await wash_soba(3) .then(r=>console.log(r))
}

かなり、冗長ではあるが、ひとまず、A、Bが定義できた。
いよいよA、Bを同時並行で実行させたい。それで、両方とも終了したときに、C:もりつけを実行したい。
さて、どうするか?

A、Bを同時並行で実行させる部分は、次のように定義できる。

(A|B)
const tempra_and_soba = async () =>{
    let t = tempra()
    let s = soba()
    await t
    await s  
}

これが終了した後に、もりつけを実行する。

(A|B)->C
function serve(resolve, cooking_time){
  console.log("もりつける");
  setTimeout(()=>resolve("天ざるそばもりつけ完了"+cooking_time),cooking_time*1000)
}
function serve_temzarusoba(sec){return new Promise((resolve)=>{serve(resolve,sec)});}

const temprasoba = async () => {
await tempra_and_soba() 
await serve_temzarusoba(5) .then(r=>console.log(r))
}

実行結果

"そば計量完了2"
"てんぷら素材準備完了5"
"そばゆで完了7"
"そばあらい完了3"
"てんぷら完了10"
"天ざるそばもりつけ完了5"

これで、うまい天ざるが食べられる。めでたし。

おまけ

お蕎麦屋さんを作ってみました。

image.png

お客さんが食べる時間などは省略。

sobaya.js
function prepare(resolve, cooking_time){
  /*console.log("てんぷら素材準備開始");*/
  setTimeout(()=>resolve("てんぷら素材準備完了"+cooking_time),cooking_time*1000)
}

function fry(resolve, cooking_time){
  /* console.log("あげる") */;
  setTimeout(()=>resolve("てんぷら完了"+cooking_time),cooking_time*1000)
}

function measure(resolve, cooking_time){
  /* console.log("そばをはかる") */;
  setTimeout(()=>resolve("そば計量完了"+cooking_time),cooking_time*1000)
}

function boil(resolve, cooking_time){
  /* console.log("そばをゆでる") */;
  setTimeout(()=>resolve("そばゆで完了"+cooking_time),cooking_time*1000)
}

function wash(resolve, cooking_time){
  /* console.log("そばをあらう") */;
  setTimeout(()=>resolve("そばあらい完了"+cooking_time),cooking_time*1000)
}

function serve(resolve, cooking_time){
  /* console.log("もりつける") */;
  setTimeout(()=>resolve("もりつけ完了"+cooking_time),cooking_time*1000)
}

function open_shop(resolve, reject, sales_time){
  /* console.log("開店") */
  setTimeout(()=>{if (this.order==0) {resolve("お店終了"+sales_time)} else {reject("まだお客さんいるけど、お店終了"+sales_time)}},sales_time*1000)
}

function open_sobaya(sec){return new Promise((resolve,reject)=>{open_shop(resolve,reject,sec)});}

function prepare_tempra(sec){return new Promise((resolve)=>{prepare(resolve,sec)});}
function fry_tempra(sec){return new Promise((resolve)=>{fry(resolve,sec)});}
function measure_soba(sec){return new Promise((resolve)=>{measure(resolve,sec)});}
function boil_soba(sec){return new Promise((resolve)=>{boil(resolve,sec)});}
function wash_soba(sec){return new Promise((resolve)=>{wash(resolve,sec)});}
function serve_soba(sec){return new Promise((resolve)=>{serve(resolve,sec)});}

const tempra = async () => {
  await prepare_tempra(5).then(r=>console.log(r))
  await fry_tempra(10).then(r=>console.log(r))
}

const soba = async () => {
  await measure_soba(2).then(r=>console.log(r)) 
  await boil_soba(7) .then(r=>console.log(r))
  await wash_soba(3) .then(r=>console.log(r))
}

const tempra_and_soba = async () =>{
  let t = tempra()
  let s = soba()
  await t
  await s   
}

const temprasoba = async () => {
  await tempra_and_soba() 
  await serve_soba(5) .then(r=>console.log(r))
}

const zarusoba = async () => {
  await soba() 
  await serve_soba(5) .then(r=>console.log(r))
}

const sobaya = async () =>{
  console.log("開店")
  let shop = open_sobaya(90).then(r=>console.log(r)).catch(r=>console.log(r))

  console.log("一人目")
  order=1
  await temprasoba()
  order=0
  console.log("二人目")
  order=1
  await zarusoba()
  order=0
  console.log("三人目")
  order=1
  await temprasoba()
  order=0
}

var order=0
sobaya()

実行結果。

"開店"
"一人目"
"そば計量完了2"
"てんぷら素材準備完了5"
"そばゆで完了7"
"そばあらい完了3"
"てんぷら完了10"
"もりつけ完了5"
"二人目"
"そば計量完了2"
"そばゆで完了7"
"そばあらい完了3"
"もりつけ完了5"
"三人目"
"そば計量完了2"
"てんぷら素材準備完了5"
"そばゆで完了7"
"そばあらい完了3"
"てんぷら完了10"
"もりつけ完了5"
"お店終了90"

以上、Promiseを使った動作サンプルでした。

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

JavaScript: 一見メモ化しにくいような数列もメモ化できるかも? たとえば素数とか

JavaScript: クロージャーでメモ化してくれる関数を作ってみたという記事を以前書きました。

前記事のまとめ : 漸化式でかけるような数列は、汎用のメモ化関数でメモ化できる。

例えば:

//フィボナッチ数列
const fibo = n =>
  (n === 0)? 0
  : (n === 1)? 1
  : fibo(n-1) + fibo(n-2)

//階乗
const facto = n =>
  (n === 0)? 1
  : (n === 1)? 1
  : n * facto(n-1)

は、汎用のメモ化関数 memoizer を使って:

//フィボナッチ数列
const fibo = memoizer( [0,1] )( a => n => a(n-1) + a(n-2) )

//階乗
const facto = memoizer( [1,1] )( a => n => n * a(n-1) )

とすると、計算した値をメモ化することで高速で計算できる、ということでした。

でも...ぶっちゃけそんなに使わなくね?

わあ、めっちゃ便利!
と当時は思ったのですが、 んじゃあそれから今まで、業務や個人的にでも使ったか?といわれると、全然使ってません。
考えるに理由はふたつ。

  1. メモ化が必要なケースになかなか出会わない
  2. 単純な漸化式で表わされるような数式ってなかなか思いつかない

1はまあしょうがないとして、2のほうはもうちょっと何とかならないかなあ、と思ったわけです。
数学とか得意な人は使いまくれるのでしょうが、漸化式といっても知ってるのはここにあるフィボナッチと階乗くらいで、あと何にも思いつかない...

漸化式で表わせないときは、便利な汎用のmemoizer を使うのをあきらめて個別にメモ化関数を書いていたんだけど、気がついたら同じようなことをしてるし、
あれ? これって思いこみ?

漸化式でなくてもよくね?

というのが、この記事の主旨です。今のところ予想です。

例えば 素数列。

面倒くさそうで漸化式で表わせなさそうなのということで選びました。

漸化式では表わせないようですが、今まで求めた数列から次の値が計算できる、と考えれば望みはありそうです。

できあがったやつはそれなりに実用になったほうがいいので:

  • 素数かどうかの調べ方 : 試し割り
    • あらかじめ2と3の倍数を除いた素数候補から
    • その数の平方根以下の素数で割って試す

ということでやってみます。

こんな風にしたい:

// 素数列の初期数列。とりあえず最初のみっつだけ。
const primeInits = [2, 3, 5]

// 漸化式っぽいもの。
// 素数列 a の n 番目の素数は、
//  n-1 番目の素数 から作られる素数候補 を順に試して、
//  試し割りで最初に割り切れなかったやつ、という意味。
const primeRule = a => n => {
  for (const nominee of nomineeG( a(n - 1) ) ) {
    if ( didPassTrialDivision( a )( nominee ) ) return nominee
  }
}

// 素数のメモ化関数を作る
// primeAt(n)で 素数列の n 番目の素数を返します。
const primeAt = memoizer( primeInits )( primeRule )

こんな風になればいいなと。今のところ希望です。

以下で、未定義の、まだふわっとしているところをちゃんと詰めていきます。

素数候補を返すジェネレータ

引数の 素数 aPrime より大きくて、2の倍数でも3の倍数でもないものが素数候補です。

const nomineeG = aPrime => function*(){
  let m = Math.ceil( aPrime / 6 ) * 6
  if ( aPrime === m - 5 ) yield m - 1
  while (true) {
    yield  m + 1
    yield  m + 5
    m = m + 6
  }
}()

試し割りで割り切れない(=素数)かどうかを返す関数

引数に素数列 a と 素数候補 nominee をとって、nomineeの平方根以下の a の要素で試し割りして、割り切れちゃったら偽、割り切れなかったら真を返します。

const didPassTrialDivision = a => nominee => {
  const end = Math.sqrt( nominee ) 
  for (let i = 0; a(i) <= end; i++){
    if (nominee % a(i) === 0) return false
  }
  return true
}

さあこれで実装は終ったはずです。ちゃんと動くかな?

使ってみる

とりあえず、30個の素数列を求めてみました。

[...Array(30).keys()].map(primeAt)
/*
[
    2,   3,   5,  7, 11, 13, 17,  19,  23,
   29,  31,  37, 41, 43, 47, 53,  59,  61,
   67,  71,  73, 79, 83, 89, 97, 101, 103,
  107, 109, 113
]
*/

ちゃんと動いているようです。やった!

ということでこの例から言えること...

今回のまとめ

漸化式で表わせない、表わしにくいものでも、前に求めた数列の値から次の値が求められるのなら、汎用のメモ化関数でのメモ化はできそうです。

今回使ったコードです。まとめて。

// 汎用のメモ化関数
const memoizer = inits => rule =>{
  const memo = [...inits]
  const a = n => {
    if(n >= memo.length) {
      for(let i = memo.length; i <= n; i++) {
        memo[i] = rule( a )( i )
      }
    }
    return memo[n]  
  }
  return a
}

// memoizer を特化して、素数列のメモ化関数を作る
const primeInits = [2, 3, 5]

const primeRule = a => n => {
  for (const nominee of nomineeG( a(n - 1) ) ) {
    if ( didPassTrialDivision( a )( nominee ) ) return nominee
  }
}

const primeAt = memoizer( primeInits )( primeRule )

// 素数列のメモ化関数のための補助的な関数
const nomineeG = aPrime => function*(){
  let m = Math.ceil( aPrime / 6 ) * 6
  if ( aPrime === m - 5 ) yield m - 1
  while (true) {
    yield  m + 1
    yield  m + 5
    m = m + 6
  }
}()

const didPassTrialDivision = a => nominee => {
  const end = Math.sqrt( nominee ) 
  for (let i = 0; a(i) <= end; i++){
    if (nominee % a(i) === 0) return false
  }
  return true
}

// 使ってみる

[...Array(30).keys()].map(primeAt)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む