6時だョ!!全員集合!!

Rails・JavaScrictを中心にアウトプットします。

2022年5月29日 アルゴリズム問題をTypeScriptやRubyで解く (by やの)

問題

https://algo-method.com/tasks/213

◇ 問題の意図

配列の中から一番大きな数字を取り出してほしいようです。


Rubyで解いてみる

◇ 解く過程
◇ 提出したコード
n = gets.to_i
a = gets.split.map(&:to_i)

puts a.max


TypeScriptで解いてみる

◇ 解く過程
  • 一瞬命名に悩みこちらを参考にしました。
    • 『関数 命名 get fetch』検索でヒットした記事
  • 問題を解くための関数を検索
    • JavaScript 配列 大きい要素』検索でヒットした記事 - MDN
    • Math.max()の引数には配列を渡すとNaNが返されるため、スプレッド構文で展開する必要がありそうです。
◇ 提出したコード
import * as fs from 'fs'
const input       = fs.readFileSync("/dev/stdin", "utf8")
const [nv, alist] = input.split("\n")

const [n, v]      = nv.split(" ").map(Number)
const a           = alist.split(" ").map(Number)

type SelectMaxNumber = {
    (array: number[]): number;
}

const selectMaxNumber: SelectMaxNumber = (array) => {
    return Math.max(...array);
}

console.log(selectMaxNumber(a));


コメント

Rubyは超シンプルになりましたねー。笑
TypeScriptは書いていて楽しいです!

頭の体操になりました〜! 今日も一日頑張りま〜〜す♪

2022年5月21日 番外編アルゴ式

今回の問題

  • 配列の要素と右隣の数字を比較して右隣が大きいケースの回数をカウントするという内容です。

解答

yano's answer

  • Array.prototype.reduce()を使用しました。
  • countを出す・配列のindexを使って操作する点などでreduceが妥当と判断しました。
//(入力受け取り 省略)
type CountUpNumberInArray = {
  (numArray: number[]): number
}

const countUpNumberInArray: CountUpNumberInArray = (numArray) => 
  numArray.reduce((accumulator, currentValue, index, array) => {
    return array[index] < array[index + 1] ? accumulator += 1 : accumulator += 0
  }, 0);

console.log(countUpNumberInArray(a));
  • 関数式の後にreturnを書いた方がわかりやすいかもしれません。
  • reduceの第二引数は使用しないため、アンダースコアで省略した方がいいです。

修正

const countUpNumberInArray: CountUpNumberInArray = (numArray) => {
  return numArray.reduce((accumulator, _, index, array) => {
    return array[index] < array[index + 1] ? accumulator += 1 : accumulator += 0
  }, 0);
}
  • 命名ももう少し拘れると良さそうです。
Rubyでやってみる
  • for in文パターン
n = gets.to_i
a = gets.split.map(&:to_i)

count = 0

for i in 0..n do
  count += 1 if a[i].to_i < a[i + 1].to_i
end

puts count
n = gets.to_i
a = gets.split.map(&:to_i)

count = 0

a.each_with_index do |value, index|
  count += 1 if a[index] < a[index +1].to_i
  if index == a.size - 1
    puts count
  end
end

yuki's answer

  • 素早く命名するのが苦手だと感じました。今後、改善していきます。(現時点の命名も本当に分かりやすいか疑問)
  • reduceを使えば、配列の2つの要素を比べられるので、reduceを使えば良かったです。関数型プログラミングをなるべく自分のコードに取り入れたいと思いました。
import * as fs from 'fs'
const input       = fs.readFileSync("/dev/stdin", "utf8")
const [nv, alist] = input.split("\n")

const [n, ..._]  = nv.split(" ").map(Number)
const list        = alist.split(" ").map(Number)

const countRightThanLeft = (list: number[]): number => {
  let count = 0;
  for (let i = 1; i < list.length; i++) {
    if (list[i-1] < list[i]) {
      count++;
    }
  }
  return count;
}

console.log(countRightThanLeft(list));

yui's answer

import * as fs from 'fs'
const input       = fs.readFileSync("/dev/stdin", "utf8")
const [nv, alist] = input.split("\n")

const [n,v,..._]  = nv.split(" ").map(Number)
const list        = alist.split(" ").map(Number)

type ArrayType = {
    (previousValue: number, currentValue: number, currentIndex: number, array: number[]): number;
}

const reducer: ArrayType = (previousValue, _, currentIndex, array) => {
    const count =  (array[currentIndex] < array[currentIndex + 1]) ? 1 : 0;
    const returnNumber = previousValue + count;
    return returnNumber;
}

console.log(list.reduce(reducer, 0));
  • 今回は、2つの要素の値を比較したいのでreduce()を使いました。
  • reduceメソッドは、配列のそれぞれの要素に対して、順番通りに、コールバック関数を呼び出します。コールバック関数の引数には、累積値, 要素, インデックス, 配列を渡すことが出来ます。配列のすべての要素に対してコールバック関数を実行した結果が戻り値となります。

  • 最初、変数にアノテーションを記述していましたが、型推論を利用するように変更しました。

    • プリミティブ型の値を使うときは、アノテーションを書かずに型推論を使う方が良いです。なぜなら、間違えて型定義してしまうことを防ぐことができるからです。
    • 対して、オブジェクトや関数の引数などはアノテーションを書く方がいいです。オブジェクトを変数に入れた時、プロパティにどんな値が入っているかに関わらず単なるobject型型推論されてしまうからです。
  • 関数の引数として、アンダーバー_が使われている記述があります。これは、使われない引数であることを明示しています。使われない値であるが、単純に引数として書く必要があるため、アンダーバーを使用しています。

アンダーバーのみの変数の意味

参考

アルゴ式

2022年5月20日 投稿機能を作成

今回実装した機能

  • 投稿一覧ページにログインユーザーの新規投稿ページへ遷移するボタンを実装 Image from Gyazo

仕様

  • userとpostを紐付ける

  • new Postリンクをクリックすると、投稿フォームが出る

  • 投稿に成功したら、投稿一覧に遷移する

  • 投稿に成功したら、「投稿に成功しました」というフラッシュメッセージを出す

実装を通した気づき

Yano
  • 外部キーの途中追加・・・rails g migration add_user_id_to_posts
    • migrationファイルに追記
class AddUserIdToPosts < ActiveRecord::Migration[6.0]
  def change
    add_reference :posts, :user, foreign_key: true
  end
end
  • ログインしているユーザーの持つpostを新規作成
    • saveに成功した場合はユーザーの投稿一覧ページに遷移させる
    • user_posts_path(current_user)とする。
    • save!とすると投稿に失敗した場合に例外を返す。falseの処理がある場合はsaveを使用する。
  def create
    @post = current_user.posts.build(post_params)
    if @post.save
      redirect_to user_posts_path(current_user)
    else
      render 'new'
    end
  end
Yui
  • current_userは、sorceryのメソッドで現在のユーザーを取得できます。
  • ルーティングの設計方法が完全に理解できていないことに気づきました。
    • 今回投稿はユーザーモデルに紐づくためネストして記述しました。しかし、それだけだと投稿一覧ページに遷移出来ずエラーが発生しました。
    • そこで、ネストしないユーザーに紐づかないルーティングを追加するとうまく遷移できるようになりました。
Rails.application.routes.draw do
  resources :users do
    resources :posts
  end
  resources :posts
end
Yuki
  • 良いURIの設計で重要な原則は、「覚えやすく、どんな機能を持つURIなのかがひと目でわかる」ことです。
# POSTリクエストで以下のURLにアクセスした場合、
# アクセスしたユーザーのポストを作成する
/users/user_id/posts
  • ログインした時に、投稿一覧ページになっていた方が良いので、redirect_toのパスを投稿一覧ページにしました。
redirect_back_or_to user_posts_path(@user), notice: 'Login successful'

参考

Simple Password Authentication-Sorcery

RESTful APIのURI設計(エンドポイント設計)

2022年5月19日 ログイン機能 ・サインアップ機能を作成

テーブル構成

◆ usersテーブル

name:string email: string crypted_password: string salt: string

◆ postsテーブル

title:string body: text user_id:bigint(references)

◆ commentsテーブル

body: text text: string

実装

ログイン機能・サインアップ機能をsorceryで実装しました。
Simple Password Authentication

実装を通した気づき

Yano
  • debuggerで値の中身を確認することが出来ます。
  • "@user"やparamsと叩くなどして、どういったパラメータを受け取っているかなどでミスを発見しました。
[2, 11] in /Users/yanokouhei/workspace/rails-free/free/app/controllers/user_sessions_controller.rb
    2:   skip_before_action :require_login, only: [:new, :create]
    3: 
    4:   def create
    5:     @user = login(params[:email], params[:password])
    6:     debugger
=>  7:     if @user
    8:       redirect_back_or_to(:users, notice: 'Login successful')
    9:     else
   10:       flash.now[:alert] = 'Login failed'
   11:       render action: 'new'
(byebug) @user
#<User id: 3, email: "yano2@yano", crypted_password: "$2a$10$4TIvRfrA3S2NlbfGzkrs/.zr2hjnzjh2UYKfGMl3VFe...", salt: "_PMqGaxonoZgeF8M5k1z", created_at: "2022-05-19 00:33:14", updated_at: "2022-05-19 00:33:14">

コロンの位置などは注意した方が良いです。

  has_many :posts, dependent: :destroy
Yui
Yuki
  • scaffoldで生成したファイルに処理内容が自動的に書かれているのがすごいと感じました。
  • strongパラメータにちゃんと値が渡っているか気をつけるべきです。
  def user_params
    params.require(:user).permit(:email, :password, :password_confirmation)
  end

明日やること

ユーザーに紐付けた投稿機能やります!

参考

Simple Password Authentication-Sorcery

2022年5月17日 りあクト! 第8章 コンポーネントのメンタルモデル (p.126~)

Reactの差分検出処理エンジン

Reactではstateの値とpropsの値に差分を検出すると、コンポーネントレンダリング処理を再実行します。

stateとprops

  • stateは、コンポーネントが持つ状態のことです。
  • stateでは、エラーがあるか、モーダルウィンドウが開いているか、ボタンを押せるかなどの状態を管理します。
  • Reactの関数コンポーネントでstateを使うためには、useStateという関数を使います。

    • const [count, setCount] = useState(初期値);
    • countが、stateを持った変数になります。
    • setCountには、countというstateを更新するための関数が分割代入されます。
  • propsは、親コンポーネントから子コンポーネントに渡すことができる引数のようなものです。
  • stateの値をpropsという仕組みを使って、子コンポーネントに渡すこともできます。

stateとpropsの受け渡し

import "./styles.css";
import { useState } from "react";

// propsとして渡す値の型エイリアスを定義
type Books = {
  name: string;
  cost: number;
  onCount: React.Dispatch<React.SetStateAction<number>>;
};

// 関数コンポーネントに渡ってくるpropsの型と戻り値の型を定義(呼び出し可能オブジェクトを型エイリアスで定義)
type BookType = {
  ({ name, cost, onCount }: Books): JSX.Element;
};

const Book: BookType = ({ name, cost, onCount }) => { // propsを分割代入で受け取る
  return (
    <>
      <div>{name}</div>
      <div>{cost}</div>
      <button onClick={() => onCount((prevCount) => prevCount + 1)}>ボタン</button>
    </>
  );
};

export default function App() {
  const [count, setCount] = useState(300); // useStateを使ってstateを定義(初期値200)
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <Book name={"book"} cost={count} onCount={setCount} />
    </div>
  );
}
◆ Appコンポーネント
  • Bookコンポーネントを呼び出しています。
  • propsにname、cost、onCountという変数を渡しています。
    • cost、onCountはuseStateです。初期値は300です。
◆ Bookコンポーネント
  • Booksという型エイリアスを定義し、関数の引数に使用しています。
  • 関数の引数と戻り値を呼び出し可能オブジェクトを用いてBookTypesとして型定義を行っています。
  • Appコンポーネントで指定したpropsを分割代入で受け取ります。
  • Bookコンポーネント内でpropsを用いてカウント処理を実装しています。
◆ useState

useStateではstateを更新する際にsetCountを用います。

const [count, setCount] = useState(0);

const increment = () => setCount(count + 1);

上記の書き方のようにstateを使わなくてもアロー関数の引数にstateを受け取ることができるため、下記のように記述する方が好ましいです。

const increment = () => setCount((prevCount) => prevCount + 1);

参考

りあクト! TypeScriptで始めるつらくないReact開発 第3.1版【Ⅱ. React基礎編】 p.126~

2022年5月16日 りあクト! 第7章 他のフレームワークとの比較 (p.113~)

世代におけるフロントエンド技術の特徴

フロントエンド技術を世代分けすると、第1世代が prototype.jsjQuery といった DOM 操作を中心に据えたユーティリティライブラリ、第2世代がAngularJSが挙げられます。

現在はReact、Vue.js、Angularが第3世代と呼ばれています。

世代 特徴 代表的なライブラリ/フレームワーク
第1世代 DOM操作を中心に据えたユーティリティライブラリ prototype.js
jQuery
第2世代 MV*アーキテクチャを採用した、modelとのデータバインディングを行うためのフレームワーク AngularJS
Backborn.js
Knockout
第3世代 コンポーネントベースアーキテクチャフレームワーク React
Angular
Vue.js

npm trends というサイトで最新のダウンロード数を見ることが出来ます。 https://www.npmtrends.com/angular-vs-react-vs-vue

Angular

◆ メリット
◆ デメリット
  • 覚えることが多く、学習コストが高いです。そして、一から十までフレームワークが決めたやり方の冗長な記述を強いられます。
  • コンポーネントベースといいつつもモジュール、サービス、プロバイダなど覚えるべき概念が多い。
  • コンポーネントを作るにはHTMLテンプレート、CSSスタイルシートスクリプトの3つのファイルが必要 (Vue.jsはシングルファイルコンポーネントです。)
  • 非同期処理やイベントを扱う際に、RxJSというライブラリを使う(可読性が低いと言われています。)
  • TypeScriptのバージョンが上がっても、Angularがそれを追随するまで最新の機能を使えない。

Vue.js

◆ メリット
  • AngularJSの機能から必要最低限の機能に絞った軽量なフレームワークである。
  • Reactのコンポーネントアーキテクチャを取り入れている。
  • HTMLベースで馴染みやすい設計で、仮想DOMを採用してパフォーマンスを向上させる改良も加えられている。
◆ デメリット
  • シングルファイルコンポーネントは長期的に見るとコンポーネントの再分割を難しくリファクタリングがしづらい(大規模開発に適していない)
  • 色々なフレームワークやライブラリの良いところを取りいれているので、一貫した思想がない。思想がないことが思想である。ポジティブに捉えると、変化に柔軟です。
  • コミュニティベースのプロジェクトなので、大企業が後ろ盾にいるReactやAngularに比べると、サポートの安心感がない。
  • Vue.jsの特有のエラーがあるため、ルールやエラー構文を覚えるまで少々開発がしづらい。

Web Componentsとは

ウェブコンポーネントは、再利用可能なカスタム要素を作成し、ウェブアプリの中で利用するための、一連のテクノロジーです。

https://developer.mozilla.org/ja/docs/Web/Web_Components

ReactとWeb Componentsは異なる課題を解決する為に構築されました。 Web Components はコンポーネントをパッケージ化して、高い再利用性を与えます。 一方で React は DOM とデータを同期させる為の宣言型のライブラリを提供しています。 Web Components内でReactを使用することも、React内でWeb Components を使用することも、あるいはその両方を行うこともできます。

ただ、あまり使用はされないそうです。  

https://ja.reactjs.org/docs/web-components.html

参考

りあクト! TypeScriptで始めるつらくないReact開発 第3.1版【Ⅱ. React基礎編】 p.113~

2022年5月14日 アルゴ式 番外編

Ryuji's answer

TypeScript

import * as fs from 'fs'
const input       = fs.readFileSync("/dev/stdin", "utf8")
const [nv, alist] = input.split("\n")

const [n,v,..._]  = nv.split(" ").map(Number)
const list        = alist.split(" ").map(Number)

type LastIndex = {
  (numArray: number[], v: number): number
};

const lastIndex: LastIndex = (numArray, v) => {
  return numArray.lastIndexOf(v);
};

console.log(lastIndex(list, v));
  • javascript 配列インデックス 取得で検索したところArray.prototype.indexOf() のメソッドがヒットし、javascript indexOf() 配列で再検索したら、lastIndexOf()の説明の記事がヒットしたのでjavascript lastindexof 配列で再再建策したところ、Array.prototype.lastIndexOf()のMDN)がヒットし使用しました。
  • 命名がよくないので次回は命名をそれを読んだだけでわかるほど具体的で読めば理解できるようなものを選ぶようにします。

Yano's answer

  • 『IndexOf 最後から JavaScript』と検索したところlastIndexOfという関数があることがわかりました。
  • 関数の命名(fetchIndexMatchTheLastV)は『最後のVとマッチする』を英訳すると"match the last V"と出てきたので命名しました。
import * as fs from 'fs'
const input       = fs.readFileSync("/dev/stdin", "utf8")
const [nv, alist] = input.split("\n")

const [n, v]      = nv.split(" ").map(Number)
const a           = alist.split(" ").map(Number)

type FetchIndexMatchTheLastV = {
    (v: number, a: number[]): number;
}

const fetchIndexMatchTheLastV: FetchIndexMatchTheLastV = (v, a) => {
    return a.lastIndexOf(v);
}

console.log(fetchIndexMatchTheLastV(v, a));
  • 命名にtheなどの冠詞は使わない方が良いそうです。

Yui's answer

import * as fs from 'fs' // fsモジュールは、Node.jsでファイルの読み書きを行うための基本的な関数を提供するモジュール
const input       = fs.readFileSync("/dev/stdin", "utf8") // 同期形式でファイルを読み込むreadFileSyncメソッドです。今回は入力値を受け取ります。
const [nv, alist] = input.split("\n") // 受け取った入力を改行で分割代入
const [n,v,..._]  = nv.split(" ").map(Number) // 要素数nと探索に使う整数vを空白文字で分割してからそれぞれの変数に代入します(その時mapメソッドで数値に変換)
const list      = alist.split(" ").map(Number) // 受け取った配列の要素をnumber型に変換

type ArrayType = {
    (list: number[], v: number): number;
}

const arraySearchIndex: ArrayType = (list, v) => {
    return list.lastIndexOf(v);
};

console.log(arraySearchIndex(list, v));
  • 今回は、型エイリアスを定義し、関数に適用しました。
  • javascript 何番目にあるかを調べる」で調べたところindexOfメソッドに辿り着き、特に配列の最後の要素から探索するlastIndexOfメソッドを見つけました。
  • lastIndexOfメソッドは、与えられた要素が配列内で見つかった最後のインデックス番号を返し、見つからなければ-1を返します。
  • 最初、問題の意図を正しく理解していなかったため、なかなか正解にならず苦戦しました。
    • 具体的には、「最も右にあるVは前から何番目にあるかを出力」という部分をインデックス0番を含めない順番で数えようとしていました。
    • しかし、「N 個の整数のうち先頭の要素 A[0]を、前から0番目であると数えることとします。」と書かれていることからインデックス番号0番目から何番目であるかを調べれば要件を満たせたと反省しました。
    • 問題を正しく読むように心がけたいと思います。

Yuno's answer

import * as fs from 'fs'
const input       = fs.readFileSync("/dev/stdin", "utf8")
const [nv, alist] = input.split("\n")

const [n,v,..._] = nv.split(" ").map(Number)
const list:number[] = alist.split(" ").map(Number)

const selectLastIndexOfIntegers = (list:number[],v:number)=>{
    return list.lastIndexOf(v);
}
console.log(selectLastIndexOfIntegers(list,v))

Yuki's answer

  • lastIndexOfを知らなかったので、for文とif文でゴリ押ししました。
  • 標準でメソッドが用意してあるので、そちらを使った方が良いです。
  • 良い命名をつける、適したメソッドを見つけるのが今後の課題です。
import * as fs from 'fs'
const input       = fs.readFileSync("/dev/stdin", "utf8")
const [nv, alist] = input.split("\n")

const [n,v,..._]  = nv.split(" ").map(Number)
const list        = alist.split(" ").map(Number)

type IdentifyMostRightPosition = {
  (v: number, list: number[]): number;
};

const identifyMostRightPosition: IdentifyMostRightPosition = (
  v, 
  list
) => {
 let mostRightPosition = -1;
 for(let i = 0; i < list.length; i++) {
   if(list[i] === v) {
     mostRightPosition = i;
   }
 }
 return mostRightPosition
};


console.log(identifyMostRightPosition(v, list));

参考

アルゴ式