徒然なるままに

現在、海外にWebエンジニアになるべくカナダのカレッジに通い中。現地の生活やプログラミング、感じたことを気ままに書きます。

データストラクチャー Arrays

Data Structure Array

Static arrayとDynamic arrayがある。Jsなどのhigh level languageでは特に意識することないが、low level languageなどでは気にする必要がある。 JavaでいうArrayはstatic / ArrayListはdynamicにあたる。

ArraysのBigO

Static Dynamic Big O
lookup lookup O(1)
push append O(1)
insert insert O(n)
delete delete O(n)

※appendはO(n)になり得る。

Ex)

const string = ['a', 'b', 'c', 'd', 'e'];
// 4 * 4 の16bytesのstorage

strings.push('e');  // O(1)
strings.push();  // O(1)
strings.unshift('x');  // O(n)
strings.splice(2, 0, 'e');  // O(n)

unshiftとspliceがO(n)になる理由は、値が配列の先頭または指定箇所に代入されることによって、 それまでのindexの位置がループによって書き換えられるため。

class MyArray{
    constructor() {
        this.length = 0;
        this.data = {};
    }

    get(index) {
        return this.data[index];
    }

    push(item) {
        this.data[this.length] = item;
        this.length++;
        return this.length;
    }

    pop(){
        const lastItem = this.data[this.length -1];
        delete this.data[this.length-1];
        this.length--;
        return lastItem;
    }

    delete(index) {
        const item = this.data[index];
        this.shuftItems(index);
        return item;
    }

    shuftItems(index) {
        for(let i = index; i < this.length -1; i++){
            this.data[i] = this.data[i+1];
        }
        delete this.data[this.length-1];
        this.length--;
    }
}

const newArray = new MyArray();
newArray.push('hi');
newArray.push('world');
newArray.pop();
console.log(newArray);

Ex1) Reverse String

const reverseString = (string) => {
    let arr = string.split("");
    let reverseArr = [];
    for(let i = 0; i < arr.length; i++){
        reverseArr.unshift(arr[i]);
    }

    console.log(reverseArr.join(''))
}

reverseString("apple is good");

const reverseString2 = (string) => {
    // check input
    if(!string || string.length < 2 || typeof string !== 'string') {
        return "that is not good";
    }

    const backwords = [];
    const totalItems = string.length - 1;
    for(let i = totalItems; i >= 0; i--){
        backwords.push(string[i]);
    }
    return backwords.join('');
}

const reverseString3 = (str) => {
    return str.split('').reverse().join();
}

const reverseString4 = (str) => str.split('').reverse().join();

const reverseString5 = str => [...str].reverse().join();

console.log(reverseString2("banana is soso"));

Data Structureの問題を解く際に、inputのチェックをすることが大事。

EX2) MergeSortedArray

mergeSortedArrays([0,3,4,31], [4,6,30]);


const mergeSortedArrays = (arr1, arr2) => {
    const arr = arr1.concat(arr2);
    arr.sort((a, b) => a-b);
    console.log(arr.join(','));
}


const mergeSortedArrays1 = (arr1, arr2) => {
    const mergedArray = [];
    let array1Item = arr1[0];
    let array2Item = arr2[0];
    let i = 1;
    let j = 1;

    // check input
    if(arr1.length === 0) {
        return arr2;
    }
    if(arr2.length === 0){
        return arr1;
    }

    while(array1Item || array2Item) {
        if(!array2Item || array1Item < array2Item) {
            mergedArray.push(array1Item);
            array1Item = arr1[i];
            i++;
        } else {
            mergedArray.push(array2Item);
            array2Item = arr2[j];
            j++;
        }
    }
    return mergedArray;
}

console.log(mergeSortedArrays1([0,3,4,31], [4,6,30]));

ArraysのProsとCons

Pros Cons
Fast lookups Slow Inserts
Fast push/pop Slow deletes
Ordered Fixed size*

*static Arrayを使う場合

React-Reduxアプリケーション開発入門まとめ

概要

こんにちはtogashoです。

今回はフロントエンドエンジニアのためのReact・Reduxアプリケーション開発入門の基礎編で学んだReactとReduxの基本的なところを自分の忘備録も兼ねてをまとめてみました。

React

Facebook社が開発したライブラリー

  • DOMの変更箇所だけをレンダリングする
  • DOMの変更箇所がどこかを開発者が気にする必要がない

JSX

javascript XMLの略。

JSXの利点とは

jsでも表示できるが、JSXの方が直感的にコーディングでき可読性が高い。

文法

  • JSXが使われているときはreactをimportする必要がある。JSXを使わなければreactは必要ない。
  • {}の中では、jsが実行できる。
  • classはclassNameと定義する。
  • 属性名はキャメルケースで定義する。 onClickonChangeなど
  • returnで複数の要素を返す場合、divタグで囲む。Fragmentはdivタグで囲むのと同じ役割をするが、ブラウザ上では表示されない。

トランスパイル

JSXを実行可能なjsに変換すること。Babelが暗黙的にやっている。

Webpack

モジュールバンドラー。ソースコードを束ねて、一つのファイルにまとめて定義し、ブラウザで表示する。

コンポーネント

関数の定義によるものとクラス定義によるものがある。

// 関数定義
import React from 'react'

const Register = () => {
  return (
    <div>
      
    </div>
  )
}

export default Register
//クラス定義
import React, { Component } from 'react'

export default class Register extends Component {
  render() {
    return (
      <div>
        
      </div>
    )
  }
}

props

  • コンポーネントの属性の名前。{}で囲って値を渡す。親から子にデータを渡す。
  • immutable(変更不可能)な値
//ex)
import React from 'react'

const App = () => {
    const profiles = [
        {name: "Taro", age: 18},
        {name: "Sam", age: 21},
        {name: "Bob"},
    ]
return (
    <div>
        {
            // コンポーネントにpropsを渡す
            profiles.map((profile, index) => {
                return <User name={profile.name} age={profile.age} key={index} />
            })
        }
    </div>
)
}

// 親から渡されたpropsを引数として受け取る。
const User = (props) => {
  return (
    <div>Hi, I am {props.name}, and {props.age} years old!</div> 
  )
}

// デフォルトのpropsの設定
User.defaultProps = {
    age: 1
}

export default App

prop-types

  • propertiesに対する型のチェックをするためのパッケージ。
  • import Proptypes from 'prop-types'でimportする
  • isRequiredで必須にできる。
  • コンポーネントを設計する段階で、受け取るプロパティの型がぶれないように設計することが大事
//ex)
User.propTypes = {
    name: Proptypes.strng,
    age: Proptypes.number
}

State

  • コンポーネントの中で状態を保つこと。コンポーネント内部でしか使えない
  • mutable(変更可能)な値
  • this.stateでアクセスできる
  • this.setStateで状態を変える。setStateが実行されるとそのコールバックでrenderが再実行される
import React, { Component, Fragment } from 'react';

const App = () => (<Counter></Counter>);

class Counter extends Component {
  // constructorはcomponentの初期化時に呼ばれるコールバック
  constructor(props){
      super(props)
      this.state = { count: 0}
  }

  handlePlusButton(){
      this.setState({count: this.state.count +1 })
  }

  handleMinusButton(){
      this.setState({count: this.state.count - 1 })
  }

  render() {
    return (
        <Fragment>
            <div>count: {this.state.count}</div>
            <button onClick={this.handlePlusButton}>+1</button>
            <button onClick={this.handleMinusButton}>-1</button>
        </Fragment>
    )
  }
}

export default App;

Reduxの概要

コンポーネントの階層が大きくなった時に、容易に状態を共有する手段を提供する。

action

  • アプリケーションの中で何が起きたかを示すデータ。jsオブジェクトのこと。
  • typeというキー(ユニークでなけれならない)とタイプに対応する値を持つ。

    action creator

  • actionを定義して、それを返す関数のことをaction creatorと呼ぶ
  • typeで指定した値は、reducerでも使う。
  • viewを担当するコンポーネントがactionをimportして、あるイベントを掴んだ時に当該のaction creatorinvokeして適切な状態遷移を実行させるための仕組み。
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';

// action creator
export const increment = () => ({
    type: INCREMENT
})

export const decrement = () => ({
    type: DECREMENT
})

Reducer

  • actionが発生した時に、そのアクションに組み込まれているtypeに応じて状態をどのように変化させるのかを定義するもの
  • 状態は、コードの中ではstateという名称で扱う。
  • アプリケーションの事実上の状態を持つreducerを定義する。
// index.js
// アプリケーション内に存在する全てのreducerを総括する。全てのreducerを結合する。
//combineReducersがreducerを結合する
import { combineReducers} from 'redux';
import count from './count';

export default combineReducers({ count });
// counter.js
// actionをimportする
import { INCREMENT, DECREMENT} from '../actions/index';

//状態をの初期値を定義する
const initialState = { value: 0 }

// actionのtypeを受け取り、switch文で状態をどう変化させるのかを定義する。
export default (state = initialState, action) => {
    switch (action.type){
        case INCREMENT:
            return { value: state.value + 1}
        case DECREMENT:
            return { value: state.value - 1}
        default:
        return state
    }
}

Store

  • reducerをもとにstoreを作成する。そのstoreが全てのアプリケーション内で使えるようにする。createStore()で行う。
  • storeを全コンポーネントを参照できるようにするため、Providerreact-reduxから提供されている。
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import App from './components/App';
import './index.css';
import reducer from './reducers';

// アプリケーション内の全てのstateはこのstateに集約されている
const store = createStore(reducer)

// 既存のcomponentをProviderでラッピングする

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

connectでstateとactionsとの関連づけ

  • react-reduxが提供するconnect関数を使用して、stateやactionをコンポーネントに紐づける。viewのイベントで状態を遷移させ、遷移後の状態を再renderingする。
  • mapStateToPropsはstateの情報からこのコンポーネントで必要な物を取り出して、コンポーネント内のpropsとしてmappingする機能を持つ関数。引数には状態のtopレベルを表すstateを渡して、どういったオブジェクトをpropsに対応させるのかを関数の戻り値として定義する。
  • mapDispatchToPropsあるアクションが発生した時にreducerにtypeに応じた状態遷移を実行させる関数がdispatch。dispatch関数を引数において、このコンポーネントに必要となるdispatch関数を宣言する。
import React, { Component, Fragment } from 'react';
import { connect } from 'react-redux';
// action creatorを呼び出す。
import { increment, decrement } from '../actions';


class App extends Component {

  render() {
    const props = this.props;
    return (
        <Fragment>
            <div>value: { props.value }</div>
            <button onClick={props.increment}>+1</button>
            <button onClick={props.decrement}>-1</button>
        </Fragment>
    )
  }
}

// stateとcomponentを関連づける
const mapStateToProps = state => ({value: state.count.value});
const mapDispatchToProps = ({increment, decrement});

// 下記でも可
// const  mapDispatchToProps = dispatch => ({
//   increment: () => dispatch(increment()),
//   decrement: () => dispatch(decrement())
//})

// connectの引数にmapStateToPropsとmapDispatchToProps渡してAppに紐付ける。
export default connect(mapStateToProps, mapDispatchToProps)(App);

ご一読いただきありがとうございました。指摘事項等ございましたら、ご指摘いただけると幸いです。

天気予報アプリ(express無し)の実装

はじめに

はじめたのブログ投稿になります。現在、海外でwebエンジニアになるべくカナダのバンクーバのカレッジに通っています。勉強した内容や、カナダでの生活を記事にしていこうと思いますので、どうぞよろしくお願いいたします。

概略

この記事ではUdemyのThe Complete Node.js Developer Course(3rd Edition)のSection6~9の内容を簡単にまとめました。

ここでは、APIを取得する方法とjsonで渡された値の取得方法を学びました。

Topic

  • Callback関数
  • Fetch
  • json
  • コマンド引数
  • module(import/export)
  • 分割代入

使用ライブラリ

  • request (簡単にhttpを作成するためのライブラリ)

https://www.npmjs.com/package/request

使用API

実装

// app.js

// utilsをimport(nodejsはimportではなく、requireでモジュールを取得する)
const geocode = require('./utils/geocode');
const forecast = require('./utils/forecast');

// コマンドから渡された引数を格納する
const addres = process.argv[2];

// 引数が渡されなかったら、エラーを返す。
if(!address) {
    return console.log('please provide an address')
}
// geocodeを実行し、エラーがない場合、latitude,longitude,loactionを取得し、forecastに値を渡す。

geocode(address, (error, {latitude, longitude, location})=> {
    if(error){
        return console.log(error);
    }
    
    // geocodeから渡された、latitudeとlongitudeをもとに天気予報と降水確率を返す。
    
    forecast(latitude, longitude, (error, forecastData)=>{
        if(error) {
            return console.log(error);
        }
        console.log(location);
        console.log(forecastData);
    })
})
// geocode.js

const request = require('request')

// importしたgeocode()を呼び出す。addressにはコマンド引数が渡される。

const geocode = (address, callback) => {
    //mapboxをfetchする。
    const url = 'https://api.mapbox.com/geocoding/v5/mapbox.places/'+  encodeURIComponent(address) + '.json?access_token=pk.eyJ1IjoidGd3IiwiYSI6ImNqdXBsN3ZlOTNiM3k0M252MzA1ZzY2NTkifQ.ucwg_oXHd7KDVTHgSufvTQ';
    
    // urlが正しく、json型で返されているかを判定する。
    
    request({url: url, json: true}, (error,{body})=>{
    
        // Urlが不正な場合、errorを返す
        
        if(error){
            callback('Unable to connect location service', undefined);
            
        // body.featuresに値がない場合、エラーを返す
        
        } else if(body.features.length === 0) {
            callback('unable to find your location', undefined);
            
        // 正常に値が返ってきた場合、latitude, longitude, locationを返す
        
        } else {
            callback(undifined, {
                latitude: body.features[0].center[1];
                longitude: body.feature[0].center[0],
                location: body.features[0].place_name
            })
        }
    })
}
// forecast.js

const request = require('request')

const forecast = (latitude, longitude, callback) => {
    const url = `https://api.darksky.net/forecast/cc390e4b8d3d1951f092c99243ae915d/${latitude},${longitude}?units=si`;
    
    //渡されたurlが正しく、json型で返ってきた場合、天気予報と降水確率を返す。
    
    request({url, json: true},(error, { body })=>{
        if(error) {
            callback('unable to connect service', undefined);)
        } else if (body.error) {
            callback('unable to find location', undefined)
        } else {
            callback(undefined, `${body.daily.summary}, ${body.daily.data[0].precipProbablity}%`)
        }
    })
}

module.exports = forecast;

mapbox api (ここではcenterの緯度経緯度を取得)

features: [
{
id: "place.9391334652012190",
type: "Feature",
place_type: [
"place"
],
relevance: 1,
properties: {
wikidata: "Q100"
},
text: "Boston",
place_name: "Boston, Massachusetts, United States",
bbox: [
-71.1255750165112,
42.3196059806256,
-70.9860500028801,
42.3974009328397
],
center: [
-71.0596,
42.3605
],
geometry: {
type: "Point",
coordinates: [
-71.0596,
42.3605
]
},
context: [
{
id: "region.6776276020561540",
short_code: "US-MA",
wikidata: "Q771",
text: "Massachusetts"
},
{
id: "country.9053006287256050",
short_code: "us",
wikidata: "Q30",
text: "United States"
}
]
},

darksky api (mapboxから取得した緯度経度をもとにdaily.sammaryとdaily.data[0]. precipProbabilityを取得)

実際、日々の生活でみるデータのやり取りが、このようなフォーマットで行われています。瞬時にデータのやり取りをするコンピュータはすごいですね!

{
latitude: 42.3605,
longitude: -71.0596,
timezone: "America/New_York",
currently: {
time: 1556073863,
summary: "Overcast",
icon: "cloudy",
nearestStormDistance: 0,
precipIntensity: 0,
precipProbability: 0,
temperature: 7.82,
apparentTemperature: 6.93,
dewPoint: 6.27,
humidity: 0.9,
pressure: 1011.35,
windSpeed: 1.72,
windGust: 2.83,
windBearing: 122,
cloudCover: 1,
uvIndex: 0,
visibility: 15.59,
ozone: 331.58
},
daily: {
summary: "Light rain today through Saturday, with high temperatures rising to 20°C on Sunday.",
icon: "rain",
data: [
{
time: 1555992000,
summary: "Overcast throughout the day.",
icon: "cloudy",
sunriseTime: 1556013117,
sunsetTime: 1556062523,
moonPhase: 0.65,
precipIntensity: 0.3277,
precipIntensityMax: 2.3292,
precipIntensityMaxTime: 1555995600,
precipProbability: 1,
precipType: "rain",
temperatureHigh: 12.1,
temperatureHighTime: 1556038800,
temperatureLow: 7.68,
temperatureLowTime: 1556071200,
apparentTemperatureHigh: 12.1,
apparentTemperatureHighTime: 1556038800,
apparentTemperatureLow: 6.71,
apparentTemperatureLowTime: 1556082000,
dewPoint: 7.77,
humidity: 0.88,
pressure: 1013.36,
windSpeed: 1.72,
windGust: 5.15,
windGustTime: 1556024400,
windBearing: 43,
cloudCover: 1,
uvIndex: 4,
uvIndexTime: 1556035200,
visibility: 11.04,
ozone: 332.64,
temperatureMin: 7.68,
temperatureMinTime: 1556071200,
temperatureMax: 12.1,
temperatureMaxTime: 1556038800,
apparentTemperatureMin: 6.86,
apparentTemperatureMinTime: 1556002800,
apparentTemperatureMax: 12.1,
apparentTemperatureMaxTime: 1556038800
}

終わりに

この記事では、講座の中で紹介されていたexpressを使用しない実装方法を紹介しました。この講座の中では、expressを使って同じapiを叩く実装も紹介されているので、後日紹介できればと思います。

ご指摘いただけることがありましたら、ご指摘いただけると幸いです。お読みいただきありがとうございました。