20200523のJavaScriptに関する記事は23件です。

TypeDocの生成物をDocusaurus v2に食わせてdeployしてみた

サマリ

OSSのAPI Referenceサイトを作るにあたり、TypeScriptからTypeDocを使って、しこしこドキュメントを作っていたのですが、サイト自体をDocusaurus v2で楽して公開したいなーと思ったのでやってみた記事となります。
なお、実際に下記の様なサイト(API Referenceの部分)がコマンド2発くらいで自動生成→デプロイできるようになります。
p5.toio - API Reference Site
https://tetunori.github.io/p5.toio/

前提と注意事項

すでにTypeScriptでビルドできる環境やソースコードがあり、TypeDocも導入済みの環境を想定しています。
以下は最小手数の導入方法を書いていますが、まずはご自分のサンドボックスリポジトリで色々いじくるのをお薦めします。

Docusaurus v2概要

DocusaurusはOSSのドキュメントサイトを簡単に作ることができるFacebook謹製OSSツールですが、現在正式版のv1と平行して、いろいろ改良中のv2が公開されています。(2020/05/23時点でまだα版です。v2.0.0-alpha.55)
今回は試用もかねて新しいバージョンを使ってみました。

Docusaurus v2をインストールする

基本はDocusaurusサイトの通りに導入することになります。一発コマンドが用意されているので、こちらで簡単にインストールできます。

npx @docusaurus/init@next init website classic

Docusaurus的にはフォルダ名は何でも良いのですが、後に使うツールのためにwebsiteフォルダとしておきます。classicはテンプレート名ですが、おまじないと思ってください。

この時点でもうサイトの立ち上げが可能です。一度立ち上げてみましょう。

cd website/
yarn run start

live-serverが自動で立ち上がり、ブラウザにテストサイトが表示されたかと思います。Docusaurus v2ではダークモードが標準サポートされていたり、モダンな感じにデザインされていますね。
image.png

TypeDocのプラグインをインストールする

さて、いったんDocusaurusから離れて、開発のルートに戻ります。
TypeDocではデフォルトの形式ですと、ドキュメントサイトの体裁で出力されます。嫌いではないですが、Global/Module/Class/Interfaceと少々冗長に感じる気がしており、見る方からすると直感的な構成にはなっていないと感じていました。
そこで、今回はTypeDocの出力をDocusaurus v2フォーマットのMarkdownとして出力してくれるプラグイン、typedoc-plugin-markdownを利用しました。小さいながら、今も活発に開発が続いているリポジトリなようです。

さて、インストールは下記のとおりです。

npm install --save-dev typedoc typedoc-plugin-markdown

Markdownとして書き出し

完了したら、さっそく、Markdownとして書き出してみましょう。

Docuzaurusへ静的ドキュメントとして読ませるためには、website/docsフォルダ以下にmdファイルを配置する必要があります。なので、typedoc.jsonなどで出力先を変更しておきます。

  "out": "website/docs",

あとは、いつものtypedocの設定に加えて、プラグインオプションと、今回のmarkdownテーマオプションdocusaurus2をそれぞれ付帯してtypedocするだけです。

typedoc --plugin typedoc-plugin-markdown --theme docusaurus2

このコマンドを実行すると、指定したディレクトリ以下にhtml/jsではなく、mdファイルが並ぶことになります。とても簡単です。と同時に、Docusaurusのサイドバー上のメニュー表示を管轄するファイルwebsite/sidebar.jsが今回のmdファイルをappendしたうえで更新されます。
余談ですが、このappend、僕は不要なのでskipしたいのですが、--skipSidebarオプションがまだDocusaurus v2には非対応なようで、問答無用に上書きされます。Issueをみると、なんと、本日付けでPR→修正されているので、近日中にリリースされると思います。なので、必要な人は今からオプションをつけておくことをお勧めします。

さて、準備はこれで終わりです。最後にもう一度Docusaurusを立ち上げてみましょう。

cd website/
yarn run start

ちゃんと今回取り込んだドキュメントが表示できるようになっていますね。お疲れさまでした。体裁は・・・そんなに良くないけど、このコマンド数でしかもほぼ自動で変換してくれるなら御の字です!
image.png

必要に応じて、不要なファイルは表示しない様sidebar.jsを編集すると、無事に自分好みの公開が出来るようになります。

デプロイ

deployも簡単ですよー。GitHub Pagesなら、下記のコマンドを打てば、自動的にgh-pagesブランチにwebサイトとしてデプロイされます。

GIT_USER=<user> yarn deploy

Enjoy!

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

javaScript 文字の切り出しとソート処理のソースコード(googleスライドに対して実行)

前提

  • GASで実行しています
  • 想定しているプレゼンテーションに含まれるスライドの見出しオブジェクトにおける文字は「1-1.sample」のような形式を想定しています。
  • 上記の形式はピリオド以前が数字とハイフン、ピリオド以降がタイトルと換言できます
qiita TitleBeforePeriod.js
// グーグルスライドの任意のプレゼンテーションURLを変数に格納する
const Url = "YOUR_GOOGLE_SLIDE_PRESENTATION_URL"
// 上記変数を元に、プレゼンテーションを開く
const presentation = SlidesApp.openByUrl(Url);
// 開いたプレゼンテーションのスライド全てを変数に格納する 
const slides = presentation.getSlides();
// 関数を実行する
function slideOrganize() {
  // 配列を初期化する
  const title_before_period_list = [];
  // プレゼンテーションの1ページ目は表紙と想定
  // プレゼンテーションの2ページ目は目次と想定
  // getSlides()メソッドで取得したページはインデックス0として取得されているため、3ページ目からスライドを操作したい場合は、要素数2からを対象とする
  for (let count = 2; count <= slides.length-1; count ++){
    // 要素番号countのスライドにおけるタイトルのテキストをString型取得し、title変数へ格納する
    const title = getTitle(count);
    // 引数として渡した文字列のピリオドの位置を返すメソッドを使います
    const period_index = getPeriodIndex(title);
    // 文字の先頭から開始して、ピリオドがある位置までをsubstr()メソッドを使って切り出します
    const organize_title = title.substr(0, period_index)
    // ピリオドの位置までで切り出した文字を配列に格納します
    array.push(organize_title);
  }
  // ピリオドの前までのタイトルをソートする
  title_before_period_list.sort(function(a,b){
    if(a < b) return -1;
    if(a > b) return 1;
    return 0;
  });
  // ソートした結果をLogで出力する
  Logger.log(array);
}

// 引数はタイトルを取得したいスライドの要素番号
function getTitle(page){
  // slides[page]で操作したいスライドを要素番号を使って指定する
  // getShapes()[0]を使って、取得したスライド内にあるオブジェクトの0番目を取得する
  // Returns the list of Shape objects on the page. (引用:参照サイトより)
  // タイトルはスライド内のオブジェクトのうち、0番目にあるオブジェクトである
  const title_object = slides[page].getShapes()[0];
  // title_objectのテキストコンテンツをgetText()を使って取得する
  // テキストコンテンツをString型として取得する
  // asString() : Returns the raw text bounded by this range of the associated shape or table cell. (引用:参照サイトより)
  // page_titleには、スライドの0番目のオブジェクトである見出しオブジェクトのテキストが格納される
  // テキストはString型として格納される
  const page_title = title_object.getText().asString();
  return page_title;  
}
// 引数として受け取った文字列におけるピリオドの位置を返すメソッド
function getPeriodIndex(title) {
  // 単語を一文字ずつ配列に格納します
  const array_string = Array.from(title);
  // 上記の配列のうちピリオドがある位置をindexOf()メソッドで計算します
  const index_period = array_string.indexOf('.');
  // 受け取った文字列におけるピリオドの位置を返します
  return index_period;
}

参照

https://www.w3schools.com/jsref/jsref_substr.asp
https://developers.google.com/apps-script/reference/slides
https://developers.google.com/apps-script/reference/slides/slides-app

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

GASのスクレイピングについて教えていただきたいです

プログラミングのスクレイピングについてです。
アプリブンのサイトから、アプリのURL、名前、会社名をスクレイピングしようとしていますが、以下のようなエラーコードがでてしまいます。

どうしたらいいでしょうか?よろしくお願いいたします。

Exception: https://app-liv.jp のリクエストに失敗しました(エラー: 404)。サーバー応答の一部: <!DOCTYPE html>

<met...(応答の全文を見るには muteHttpExceptions オプションを使用してください)(行 35、ファイル「コード」)詳細表示しない

//参照サイトhttp://www.initialsite.com/w01/14311
function scrapSample() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('シート1'); //
var getUrl = 'https://app-liv.jp/business/all/0002/popular/';

//UrlFetchApp.fetch() でトップページのURLへアクセス。getContentText() でトップページのテキストデータが取得
var content = UrlFetchApp.fetch(getUrl).getContentText('UTF-8');

//new RegExp で記事URLの正規表現をitemRegexp として作成します
//class=”applink”/正規表現/g のようにオプションにgを選択して、一致するものを全てをマッチさせるようにしています。
var itemRegexp = new RegExp(//g);

//content のうち、この条件にマッチした部分をitem に配列として格納
var item = content.match(itemRegexp);
var items = [];

//item に格納したURLはaタグやclass名がついたままなのでreplace() でURLの文字列だけにしたものをitemURL へ代入
for (var i = 0; i < item.length; i++) {
var itemURL = item[i]
.replace(' .replace(/"/g,'')
.replace(/class=c-app-card-block>/,'');

//items へpush() して二次元配列として格納します。
items.push([itemURL]);
}

var spanRegexp = new RegExp(/.*?<\/span>/);
var spans = [];

for (var j = 0; j < items.length; j++) {
//UrlFetchApp.fetch() で先ほどitems に格納した記事URLへアクセスし、取得したテキストデータをitemCont へ代入します。

var spanCont = UrlFetchApp.fetch(items[j],).getContentText('UTF-8');
var span = spanCont.match(spanRegexp)[0]
spans.push([span]);

}

//sheet.getRange() で範囲を指定し、setValues() 二次元配列に格納してある記事タイトルと記事URLを書き込みます。
sheet.getRange(2, 1, spans.length, 1).setValues(spans);
sheet.getRange(2, 2, spans.length, 1).setValues(items);
}

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

Javascript -オブジェクト- 自分用ノート

オブジェクト

複数のプロパティをもつデータのまとまり。

各種データをひとまとめにして、1つの変数として扱えるデータ。

オブジェクトの作り方

❶プロパティ数が0のオブジェクトを作成して変数に保存する

書式
let 変数名 = {}

*オブジェクトは変数だけでなく定数の定義も有。



❷プロパティが1個以上あるオブジェクトを作成する

書式
let 変数名 = {プロパティ名1:データ, プロパティ名:データ, .....,プロパティ名X:データ}; 

例:let book = {title: 'javaScript入門', price: 2500, stock: 3};

プロパティ名は個数制限はない

<オブジェクトのからデータを読み取る、書き換える>。

<データを読み取る>

書式
オブジェクト.プロパティ名

例:console.log(book.stock);
 console>> 3
書式
オブジェクト名['プロパティ名']

例:console.log(book['price']);
console>> 2500

<データを書き換える>

書式
オブジェクト名.プロパティ名 = 新しいデータ;

または

オブジェクト名[プロパティ名] = 新しいデータ;




for...in文(オブジェクトのプロパティを読み取り、繰り返す文)

オブジェクトのプロパティを全て読みとることだけを目的にした繰り返し専用文。

❶for...in文を用意

書式
for(let 変数名 in オブジェクト){
処理内容
}

例for(let p in book){
   }

❷プロパティ名を読み取る

書式
p

*変数名は何でも構わないが基本的にp

❸プロパティ名データを読み取る

書式
オブジェクト[p]

例:book[p]

※book.pと書くとプロパティデータではなくpプロパティを取得してしまう(全然違う意味)

オブジェクトと配列の違い

配列:インデックス番号がついており、繰り返し処理できちんと番号順で出てくる
オブジェクト: 順序が崩れることもある

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

JSメソッド集 自分用

配列

配列の最後にデータを追加する

配列名.push(追加したいデータ)

配列の最後のデータを削除する

配列名.pop()

配列の最初のデータを削除する

配列名.shift()

配列の最初のデータ1,データ2を削除する

配列名.unshift()

テンプレート文字列

「’’(バックティック)」で囲む。

普通の文字列のと違い

・文字列中に変数を埋め込める

${変数名}

例:const total =`<li>${item}</li>`
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptで必ず出てくるDOMの正体とは

DOMとは?

いきなり結論から言います。
DOMとは階層構造のことです。
例をみていきましょう。
高校生の生徒会を想像してみてください。
頂点には生徒会長がいて、その下に委員長が複数存在します。
そしてその委員長の下にはたくさんの委員が存在します。
この階層構造自体をDOMと呼ぶのです。
これをHTMLに置き換えると
頂点(生徒会長)=body
頂点の下(委員長)=divが複数
divの下(委員)=pなどたくさん

この階層構造がDOMです。

DOMに出てくるノードって何?

結論、頂点とかを指し示す名前みたいなものです。
先ほど出した具体例でまた考えてみます。
頂点(生徒会長)=親ノード(bodyのこと)
頂点の下(委員長)=基準ノード、兄弟姉妹ノード(divのこと)
divの下(委員)=子ノード(pなどのこと)
です。

この言い方はしっかり覚えておいた方がいいです。
次回はボタンの取り扱いについてやります。

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

JavaScriptでの関数の取扱説明書

JavaScriptでの関数使用方法

rubyではdef~endで関数を使用していましたが
JSではfunction文を用います。
以下、定義です

function 関数名(引数){処理の内容}

定義だけ示されてもよく分からないと思うので具体例を交えていきます。

具体例①

function hello() {
  console.log("hello");
}

function name() {
  console.log("name");
}

hello();
name();

これを実行した結果は以下の画面です。
JavaScript.png
これは定義が分かれば理解できると思います。
では次の具体例をみていきましょう

具体例②

function sum(num1,num2){
  num1*num2;
}

let num1 = 1;
let num2 = 2;
console.log(sum(num1,num2));

これを実行した結果は以下の画面です。
JavaScript-3.png
なぜか2という結果が出力されていません。
これはそもそもreturnが入っていないからです。
rubyの場合ではreturnがなくても自動的に返り値を設定してくれましたが
JSではreturnを明確にしないとこのようなエラーがでます。
なので以下のようなコードを書くとエラーがなくなります。

function sum(num1,num2){
  return num1*num2;
}

let num1 = 1;
let num2 = 2;
console.log(sum(num1,num2));

実行結果は以下の画面になります。
JavaScript-4.png
これで正しく表記されました。

関数の種類

実は関数には2つの種類があります
①関数宣言
②無名関数
それぞれ、どのようなコードなのか、みていきましょう

関数宣言
function hello(){
  console.log('hello');
}

無名関数
let hello = function(){
    console.log('hello');
}

違いとしては無名関数は関数自体に名前がないです。
働きの違いとしてはどういう違いがあるのかみてきます。
無名関数をみてきます。以下のように記述していきます。

hello();

let hello = function(){
    console.log('hello');
};

そして実行結果は以下のようになります。
JavaScript-5.png
エラーが出てしまいました。
ここでプログラムの原理原則を思い出してください。
上から順番に式を実行していくのが原則でした。
今回はhelloが定義されていないにもかかわらず、
helloを呼び出しているのがエラーの原因です。
これを以下のように書けばうまくいきます

let hello = function(){
    console.log('hello');
}

hello();

プログラムの原則を利用して順序を逆にしました。
余談ですが関数宣言はrubyのように先に定義しなくても大丈夫です。
無名関数だけ今までと扱いが違うので注意しておきたいです。

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

初心者でもタグ1つで秒速PWA対応。GUIエディタ(また)作りました。

概要

  1. 前回記事のLGTMとストックありがとうございます!
  2. PWAって便利ですよね。
  3. PWAってなかなか複雑ですよね。
  4. Service Workerとその登録スクリプト、1つにまとまっちゃいました。
  5. manifest.jsonも動的(?)に読み込めますね。
  6. 1行だけソース書けば対応できるようになりそう
  7. できちゃったのでまたエディタつくっちゃいました(こちら)

はじめに

こんにちは。いーちゃんです。
突然ですが、あなたのサイトPWA、対応してますか?
すでに実装している人、これから実装する人、面倒でできていない人など、様々だと思います。
manifestとService Workerを書けば済むんですがそれすらも面倒だと時々思ったり...。
ということでPWA対応を秒でしていきましょう。
(例によって前置きが長いのでエディタを早く使いたい方はこちらへどうぞ)

そもそもPWA(Progressive Web Apps)って何?

Screenshot_20200522-214059.png
(Nuxt.jsのサイトより)
スマートフォンなどでWebページを見ていて、このような「ホーム画面に追加」ボタンを見かけたことはありませんか?
押してみるとあたかも普通のアプリのようにホーム画面に追加されます。サイトによっては、ネットがつながらない環境でも読み込めたりもします。利用者にとっては、いい事づくしです。

この機能を自分のページでも使えるようにするためには、PWA(Progressive Web Apps)に対応させなければなりません。
28352004-a055292c-6c4b-11e7-9c6b-a94cdc2a5458.jpg
逆に言えば、この仕様に準拠さえすればブラウザが勝手に追加してくれるので、とても楽です。
...その実装に少し手間がかかったりもするのですが。

PWAは主に、
custom – 1.png
- Web App Manifest (JSON)
- Service Worker (JavaScript)

の2つで構成されています。それぞれ、

  • Web App Manifest: サイトの名前やアイコンを定義する
  • Service Worker: キャッシュなどの処理を裏でする

という役割です。キャッシュが必要なければService Workerはファイルだけ用意してもいいのかもしれません(未検証)。
しかし、キャッシュの実装はページの2回目以降の読み込みには大きな影響を及ぼしますから、「できることなら実装しておきたい」。そう思いませんか?

Service Workerの実装

Service Workerのキャッシュの仕組み

Service Workerでは主に2回、キャッシュを取るタイミングがあります。それは、

  • はじめにページが開かれた時(=Service Workerをインストールするとき)
  • ファイルを実際に取得する時

です。Service Workerが取得したキャッシュは、ブラウザに保存されています。

Chromeだと、ここで見れたりします。
コメント 2020-05-23 133018.png
(Nuxt.jsのサイトより)

そのため、2回目以降はサーバーと通信する必要が(少)なくなるので、ページ読み込み速度が大きく改善するのです。

通信する必要がなくなるかはサイトの仕組み次第ですが。

Service Workerの登録

Service Workerの処理内容を sw.js に記述したとします。そのスクリプトをアプリに関連付けるために、またスクリプトを書きます。

普通なら 単に<script>タグにService Workerを書いたりsrc属性でsw.jsを指定するだけではうまくいかないものです。

HTMLのheadタグに以下を追記します。

<script>
if ("serviceWorker" in navigator) { // 対応ブラウザのみ処理
    navigator.serviceWorker.register("/sw.js");
}
</script>

あら簡単。
ちなみに、この登録用スクリプト自体は<script src="">での指定も可能です。

Web App Manifestには何を書く?

Web App Manifestは、アプリの名前、概要、アイコンなど、アプリ自体の挙動…というよりかは見た目を定義するようなファイルです。規格上 *.webmanifest ファイルへの定義が基本ですが、中身はJSONなので *.json への定義も許可されています。(そのため、以下manifest.jsonと呼ばせてください。)

Chrome等の拡張機能でも同名の manifest.json が用いられますが、定義すべき内容は異なります。

PWAが最低限動作するために必要な定義は、

  • アプリの名前
  • アイコン(1つ以上指定)

です。簡単。
ですが、例えば「PWAからのアクセスは別の処理を」や、「ホーム画面に置くにはアプリ名が長すぎる」など、アプリによっては情報を追記したほうが良いかもしれません。

ということで、基本的にこのようなファイルが作成できます。

{
  "name": "アプリのフルネーム",
  "short_name": "短い名前",
  "description": "アプリの概要",
  "start_url": "/起点URL",
  "theme_color": "#テーマ色",
  "icons": [
    {
      "src": "/アイコン画像のパス",
      "sizes": "192x192"
    }
  ]
}

パスのはじめに/をつけ、絶対パスを指定しています。 manifest.json はサブディレクトリから参照されることも多いので、(個人的には)絶対パスで指定しておくと便利だと思います。

アイコン画像は、最低限192x192を用意しておくことが望ましいでしょう(Googleの方針)。

さらに、テーマ色も定義しておくと、ブラウザによってはいろいろな部分の表示色を変えてくれたりもするので、統一感を出すためにも指定をおすすめします。

なお、これ以外にも様々な設定ができますが、説明は割愛させていただきます。こちらのサイトに詳しい説明がありますので、興味がありましたらこちらも参照してみてください。

manifest.jsonの登録

こちらはlinkタグ1つで登録できます。

<head>
  <link rel="manifest" href="/manifest.json">
</head>

唯一注意点があるとすれば、CSSではないのでrel="manifest"を書くことです。

個人的にはhrefなのかsrcなのかもよく迷いますが。

いざ実装。

こう見るとJavaScript書いてJSON書いて、と(文法似てるけど)なかなか大変ですね。お待たせいたしました。ここでやっと1行実装の登場です。

ということで、またエディタを作りました
(こちら)。
pwa.app.e-chan.cf_.png

前回の記事をお読み頂いていると察されているかもしれませんが、ソースコードは前回から転用してます。
必須欄を最低限打ち込んでいただければService Workerとmanifest.jsonを勝手に作ります。zipにします。ダウンロードしていただき、<script>タグ1つで動きます。

Service Workerのスクリプトを見ると、「1行実装」できる理由が見つかるかもしれませんね。

エディタ実装について

サーバー構成や言語などは前回と同じなので割愛します。コピーですから。

なんで1行でいいの?

答えは簡単。

  • manifest.jsonの読み込み
  • Service Workerの登録
  • Service Workerの処理

をService Workerのスクリプトに全部まとめちゃったからです。

何ならmanifest.jsonも消せないかな…と思い試しにBLOBのURLを渡してみましたがプロトコルがだめみたいです。難しい。
(Data URLとかならいけたり…?(未検証))

Service WorkerはDOMにアクセスできないので、manifest.jsonの読み込み、Service Workerの登録を1ファイルで..なんて通常ではできません。そのため、

  1. 普通のスクリプトとして呼び出す
  2. manifest.jsonを追加する
  3. Service Workerとして呼び出す
  4. Service Workerとして処理する

という処理をするために、(window.)navigatorの存在で処理を分岐しています。

終わりに

思いつきでService Workerの登録スクリプトと処理スクリプトをまとめてみたら、意外とうまくいっちゃいました。
「思いついたらとりあえずやってみる。」大事なのかもしれませんね。

例によってもう一度URL貼ります。(https://pwa.app.e-chan.cf/)
これまた例によってGitHubも公開しています。スパゲッティーです。

この記事の感想、あるいはご指摘等ありましたらぜひコメントよろしくお願いします!
※特に機能テストが不十分な可能性もあるので...。

ちなみに、私も参加しているOrganization、SGGのLGTMが1000超えしたようです。
いつも閲覧ありがとうございます。
Organizationのページは こちらです。ぜひご覧ください。

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

ランダムな(重複しない)ID的文字列を生成する

概要

IDとかに使う重複しない文字列を生成するメモ。
node.jsのcryptoを使う

生成方法

cryptoのrandomBytesを利用して、ランダムなBufferを生成し、それをエンコードして整形する。

  • 重複しないためにはある程度長さが必要なので20文字以上を生成するようにする(今回は20文字)。
  • エンコードはbase64。ただし+とか/が入るのでreplaceで削除
const crypto = require("crypto");
const Length = 20;

const ids = [];
console.log("生成開始");
for (let i = 0; i < 10; i++) {

    const _id = crypto.randomBytes(Length + 2).toString("base64");
    const id = _id.replace(/\W/g, '').substring(0, Length);
    ids.push(id);

}
console.log(ids);
console.log("生成終了");

実行するとこんな感じで取得できる。

生成開始
[
  '5ijpeWvWucz0TQiK0M5u',
  'gatv1ox3SkoTeq8dEOce',
  'wis9LuhRwp2E5Iq8f4Jt',
  'ZuD1MSCdAUkstja4OCVO',
  '9lWprxeENzriFYuRDk8k',
  'oyzZeimnb83fswto3qpC',
  '2mG32SOm9KaYkVlQtbOQ',
  '6XQUoKUlNWieRWBEmXq7',
  'Xyg4BBNM9gxPYzhE06cM',
  'wDdQqIguxUwhV7aHEHKT'
]
生成終了

重複チェック

一応重複チェックしてみる。

console.log("重複チェック開始");
ids.forEach((id, index) => {
    ids.forEach((id2, index2) => {
        if (id == id2 && index !== index2) {
            console.log("重複:" + id);
        }
    });
});
console.log("重複チェック終了");

10件くらいだと何やっても大体重複しませんが、1万件生成してやってみても重複しなかったので大丈夫そうでした。

以下は1,000件生成時の結果(一応)。

生成開始
[
  'KhU8XXnyNeA6NcLInDOs', '31IXFQJVJLFhheSkMSv7', 'ZkkkcmqSdMVfyA7z5jWk',
  'e29d1PifNUHuolBig0yY', '31cSIoNDqVMZO9e5qMSr', 'A2mAWgfM8NC8IeTMHC2J',
  'tx2IGh5bpUlNRzWmQgY4', 'epfcwUkdcjb1bJ2qMCP8', 'kITqkUqO6ZhnjHXE6Qo0',
  '0euYTpcRqyQ7fhIiSGej', '9eVrmlwH3goaEFgFARdT', 'LIM5sgYHCSFdMqCXX4r8',
  '7UBeOVcozIHk2ThhV50G', '8JEUiish4Ly81SNpuYkc', 'Mb3s6Xaycd2FZSlIKaQ6',
  'Q3NZNYiiAsNycXXKYyA3', 'oalTcnrH8YoTRcGkRiYS', 'XOcqrCHYRXyzaoJUJSqD',
  'lkm7XJfh8V64Ge16VrWY', 'jQtjEQhHCNbzuJqT5H5y', 'GdZY46ByCBfFjTtyInf7',
  'z0YGMmk8Bvsmb1DbSr0o', '8jDU6UULnyoOBCMDOker', 'hPoCtjTy5lY08HmUE6pG',
  'j28qcMYqmDiZXBDFVKPf', 'bPQb6lsagGvejFwLU1Tx', 'rXGXCZLJjXQvfhMiehed',
  'TJoaswumdySH3kRlyENu', 'QFECTerBcL7yqwW54vBd', 'xBb9VYDmbeZOMm8sPINU',
  'e69npMEyfvB368BHyhSa', 'd4ehXcuLs4J07Lfew6Uv', 'Bbun5vXWcVP9s5izIktf',
  'YroygL5r4DZz4b5OT0j8', '0sFbrsscezT6xAphfnmQ', 'TIMcxfa9dNJLEV0u7nf0',
  '6nooqflKNwNAeSMUr49r', 'ryuEkmwEiSyhFhUEDJqe', 'RuuU5kmyuTWhEj3385yp',
  'D5SVemMddF5iveCzNDN4', 'IFggTyHagkYB1tegJprZ', 'SAHe5u3jgKseZVNNAkJA',
  'PoNiSRshvjYMuLVwOpYH', 'iM9uZNLnLcAccbWP5CyF', 'ObEQIo9jtQHz6aiVdj4g',
  'RpcHmsTr9WYRulgfC9bd', 'S2UEsLFQ0iEqHc2QERDe', 'ec2MFmF0qcu3lOhfw676',
  '204YtC6TlJYA52lTGhTp', 'CQmAL3QcbwvSF1MJZzg5', 'LPhRcCJ2P4VteyKgsN63',
  'TyMe5iB81KinWHGdXSxb', 'UlfFcgIsbel3wyJhFUTH', 'KpSTwMtqBuRMSIo1aM0V',
  'alBf8wWFCW09qqIkgLTT', 'LDNuBcSKLH2TukqQlX0B', '2De2aOP9WJc8koImVNlC',
  'tPK0oeEavqMjgBC0siyh', 'NKNv7EDQEXSeOkvy3V8P', 'DMsXhNgCgXmCP0hbkOgR',
  'OqdmW3iqg13WLQ1nT8YH', '3rTUwtTTguR6v6Dxq7aZ', '5npiptejaxR8LPxopATd',
  'dEqXyAP7KVSJSdG7mdLb', 'mJfjEA5DbIdnkTzKTLkp', 'ORiVGtw5gnpFn9r7k4MW',
  'Li3xBB5Trlcn13Tsk0Qk', 'QIF74WIRUGrOHJvDAgBX', 'ylT05Bl4ZKKQ62rzjTQm',
  '0RYaQenLVXxcA5RM9OMp', 'm914UgXvXq3UO7swGeTR', 'q7DlJqCUql44JTMOqgj4',
  'scnAzdDH756t8muo35Hs', 'hukcWg9hIg8aIM0v5hOz', 'zz27UhFgPPkOz5h13mOD',
  'ob2z1Lj0GHpE39b4izEj', 'aYbEBJL52uZUB1Mbsnq9', 'PArJkEjz5x5J4UYD8ifR',
  'LnhLBzFIjjNoVhe7bgcA', 'kdpgNl8M2WhX68rNG7vI', 'Ke9YizGC17hslrivhNei',
  'OvxKN5KbWkfYFeXNK8aB', 'CSLngsPPKUlk6p3ayaLn', 'Ci9J3DJalgHae2g04r0y',
  '5FBlrZf9iEfGcoZjflKj', 'flWvyK1iCGygADkj5H5V', 'GWchhgbYvAj4PegEoirP',
  'bVHC7ahGiYVG9MKxecxj', 'XSc3McPRaa2ADT7KsFKJ', 'uHq38Ay6ICgaZKdUAW4U',
  'iI8teio406X0V6iGXBUQ', '4sbEbJ1ZjroTLPdAPI7J', 'ODJhSFHQHaXnaex6kwoP',
  'dR6XHHmRNJNy3hxtaI7s', 'xnMECDItfkmZUCDcKMtn', 'YePfE0tSUMB6ROHsejgU',
  'mEnFclJAezNIe9CjaFey', 'zBAjohTNQJ0ss6S8Nk6F', '0kTgqjeYkTGQ79P04mqM',
  'vNZJNgccPGbTcKvWWRsM',
  ... 900 more items
]
生成終了
重複チェック開始
重複チェック終了
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascript における「==」と「===」の違い

Javascript における「==」と「===」の違い

どちらも「左辺と右辺が等しいか?」を比較するのですが、決定的な違いは**データ型を比較するかしないかの違いです。

「==」

値のみでデータ型は比較しない。

(厳密には異なり、詳細は後述にて補足致します。)

「===」

型と値を比較します。

sample.js
   var num = 123;
   var str = '123';

   document.write(num == str);  // true
   document.write(num === str); // false

「数値型の123」と「文字列型の123」を比較した場合、データ型まで比較を行う「===」はfalse、行わない「==」はtrueとなります。
比較において「==」と「===」のどちらかを使うべきですが、基本的には「===」を使います。
その理由として
型の比較を行わない「==」はバグの原因になりやすいなど理由は多々あるようです。
ですから変数の型を意識する意味を含めて、「===」を使ってプログラムを書いく方が良さそうです。

「==」についての補足です。

前述で「==」は型の比較をしないと言いましたが、厳密に言うと
「型を特定の決まった方に合わせるように変換し、そのあと比較する。」
というニュアンスが正しいです。

例えば文字列型と数値型を「==」で比較した場合は、数値型を文字列型に自動で変換した後に、比較を行います。これはjavascriptで定義されている一定の法則で行われています。

型の自動変換表

  演算   変換
文字列と数値 文字列に変換
数値と論理 数値に変換
文字列と論理 文字列に変換
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascrip における「==」と「===」の違い

Javascript における「==」と「===」の違い

どちらも「左辺と右辺が等しいか?」を比較するのですが、決定的な違いは**データ型を比較するかしないかの違いです。

「==」

値のみでデータ型は比較しない。

(厳密には異なり、詳細は後述にて補足致します。)

「===」

型と値を比較します。

sample.js
   var num = 123;
   var str = '123';

   document.write(num == str);  // true
   document.write(num === str); // false

「数値型の123」と「文字列型の123」を比較した場合、データ型まで比較を行う「===」はfalse、行わない「==」はtrueとなります。
比較において「==」と「===」のどちらかを使うべきですが、基本的には「===」を使います。
その理由として
型の比較を行わない「==」はバグの原因になりやすいなど理由は多々あるようです。
ですから変数の型を意識する意味を含めて、「===」を使ってプログラムを書いく方が良さそうです。

「==」についての補足です。

前述で「==」は型の比較をしないと言いましたが、厳密に言うと
「型を特定の決まった方に合わせるように変換し、そのあと比較する。」
というニュアンスが正しいです。

例えば文字列型と数値型を「==」で比較した場合は、数値型を文字列型に自動で変換した後に、比較を行います。これはjavascriptで定義されている一定の法則で行われています。

型の自動変換表

  演算   変換
文字列と数値 文字列に変換
数値と論理 数値に変換
文字列と論理 文字列に変換
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

カーソルキーでプルダウンを選択する

始めに

よくセレクトボックスで操作感をよくするためにキーボードでも選択ができるようにしているサイトがありますが、具体的にどうやっているかをまとめました。

実装方法

端的にいえば、focus処理をJS側で泥臭く設定しています。もうちょっとなんとかならないのかなと思いましたが、これがスタンダードなやり方な気がしました(他にやり方があれば教えて欲しいです)。
具体的にはtabindex="0"を設定してfocusできる要素にして、後はキーイベントを拾ってそれに応じて前や後の要素をfocusさせます。
実装イメージは以下の通りです。

実装イメージ
<template lang="pug">
.select-list
  template(v-for="option in $props.options")
    //- tabindexをセットして、keydownイベントでキー操作をハンドリングする
    .select-list__item(
      :key="option.id"
      ref="elItems"
      tabindex="0"
      @click="onSelect(option.id)"
      @keydown="onItemKeyDown"
    )
      | {{ option.text }}
</template>

<script>
/** キーボードの入力コード */
const KEY_CODES = {
  ENTER: 13,
  UP: 38,
  DOWN: 40,
};

Vue.component('SelectBox', {
  data() {
    return {
      focusIndex: 0,
    };
  },
  methods: {
    focusItem(index) {
      this.$data.focusIndex = _.clamp(index, 0, this.$refs.elItems.length - 1);
      const elItem = this.$refs.elItems[this.$data.focusIndex];
      elItem.focus();
    },
    onItemKeyDown(event) {
      event.preventDefault();
      event.stopPropagation();

      // Enterキー: クリックイベントを発火させて、クリックと同じ挙動にする
      if (event.keyCode === KEY_CODES.ENTER) {
        const elItem = this.$refs.elItems[this.$data.focusIndex];
        elItem.click();
      }

      // ↑キー: 一つ上の要素をフォーカスさせる
      if (event.keyCode === KEY_CODES.UP) {
        this.focusItem(this.$data.focusIndex - 1);
        return;
      }

      // ↓キー: 一つ下の要素をフォーカスさせる
      if (event.keyCode === KEY_CODES.DOWN) {
        this.focusItem(this.$data.focusIndex + 1);
        return;
      }
    },
  },
});
</script>

実際に実装したサンプルコードは以下になります。

See the Pen カーソルキーでプルダウンを選択する by wintyo (@wintyo) on CodePen.

検索+バーチャルスクロールの場合

検索ボックスがあってそこから↓キーで項目にfocusする方法も同じようにJSでハンドリングすればいいですが、項目数が多くてバーチャルスクロールで実装した場合はかなりの鬼門でした。。。
ここではvue-virtual-scrollerのRecycleScrollerを使った際の実装方法について書いていきます。

focus対象の要素はquerySelectorで探す

何番目とかで見つけられないので、クラスを付与してそのクラスを探すようにしました。ここで注意なのは再利用しているということで使われていない要素に昔の情報が残っていることがあるので、キチンとactiveなものにだけクラスをつけるようにします。

実装イメージ
<template lang="pug">
RecycleScroller(
  ref="scroller"
  :items="_filteredOptions"
  :itemSize="40"
  keyField="id"
  v-slot="{ item, active }"
)
  .select-item(
    tabindex="0"
    :class="{ [`js-active-item-${item.id}`]: active }"
    @click="onSelect(item.id)"
    @keydown="onItemKeyDown"
  )
    | {{ item.text }}
</template>

<script>
Vue.component('SelectBox', {
  methods: {
    focusItem(index) {
      if (!this.$refs.scroller) {
        console.warn('scroller not found');
        return;
      }
      this.$data.focusIndex = _.clamp(index, 0, this._filteredOptions.length - 1);
      const option = this._filteredOptions[this.$data.focusIndex];
      // RecycleScrollerにあるエレメントから対象のDOMを探す
      const elItem = this.$refs.scroller.$el.querySelector(`.js-active-item-${option.id}`);
      if (!elItem) {
        console.warn('focus item lost!');
        return;
      }
      elItem.focus();
    },
  },
});
</script>

検索ボックスから項目に移動する際は上にスクロールしてからにする

バーチャルスクロールで一番上の要素が無い場合があるのでスクロールしてからフォーカスします。
scrollToItemはドキュメントには書かれてなかったのですが、Issueには書かれてました(結構危ないかも。。。)
https://github.com/Akryum/vue-virtual-scroller/issues/180

スクロールしてからフォーカス
if (event.keyCode === KEY_CODES.DOWN) {
  // 上にスクロールしてからフォーカスする
  this.$refs.scroller.scrollToItem(0);
  window.setTimeout(() => {
    this.focusItem(0);
  }, 10);
}

要素がblurされるたびにfocusし直す

使いまわしていくので、中身が更新されてfocusが外れてしまう場合があります。これをどうしようか色々検討したのですが、最終的にはblurされたらfocusし直すやり方が一番focusをキープできました。ただしこれでも上手くfocusできない時がありました。。。そもそもスクロールしているとfocus対象の要素が存在しない時もありますし、これが限界のような気がしました。

検討メモ

却下された他の方法についても記載しておきます。

RecycleScrollerのupdateイベントをみて更新する
updateイベントは更新後だと思っていましたが、どうやら違うような気がしていて、updateイベント後にフォーカスし直しても外れてしまうことが多々ありました。setTimeoutを挟んで一定時間置いてからfocusするという方法もなくは無いですが、それだと不安定なので却下となりました。

サンプルコード

以上の実装をしたサンプルは以下の通りです。

See the Pen カーソルキーでプルダウンする(バーチャルスクロール版) by wintyo (@wintyo) on CodePen.

終わりに

以上がカーソルキーでプルダウンを操作する方法でした。バーチャルスクロールについてはキー操作させるのはかなり難しく、中途半端な感じになってしまいました。。。vue-selectというライブラリはhover時もfocusする挙動になっていて、これにしていれば途中でfocusが外れてもそこまで気にならないのかなとか思ってきました。
ググり力が足りないだけかもしれませんが、意外とこういった記事が見当たらなかったので誰かのお役に立てれば幸いです。

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

Js 乱数 配列 For of ループ For ループ を使ったコード

環境:GAS

目的:

  • 任意の回数配列にランダムな数字(0〜9)を入れ、それを昇順にソートする。
  • 要素ごとにログで出力する
qiita arrayPractice.js
function arrayPractice() {
  let number_list = [];
  const LOOP_COUNT = 20;
  for (let count = 1; count <= LOOP_COUNT; count ++){
    number_list.push(Math.floor(Math.random()*10));
  }
  Logger.log('result for loop' + number_list)  
  let organize_list = number_list.sort()
  // For (element in array){ } の場合、Xは要素番号となります。
  // 上記方法だとLogger.log(array[element])とする必要があります
  for (let number of organize_list){
    Logger.log(number)             
  }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javaScript 乱数 配列 For of ループ For ループ を使ったコード

環境:GAS

目的:

  • 任意の回数配列にランダムな数字(0〜9)を入れ、それを昇順にソートする。
  • 要素ごとにログで出力する
qiita arrayPractice.js
function arrayPractice() {
  let number_list = [];
  const LOOP_COUNT = 20;
  for (let count = 1; count <= LOOP_COUNT; count ++){
    number_list.push(Math.floor(Math.random()*10));
  }
  Logger.log('result for loop' + number_list)  
  let organize_list = number_list.sort()
  // For (element in array){ } の場合、Xは要素番号となります。
  // 上記方法だとLogger.log(array[element])とする必要があります
  for (let number of organize_list){
    Logger.log(number)             
  }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Number(),parseInt(),parseFloat()のちがい。JavaScript 数値型への変換

数値型に変換したい時、どれ使えばいい?

数値型に変換したいけど、どれを使っていいのかわからん!
ってなったのでまとめました。

特殊な数値の表記

基礎知識として以下の表記についておさえておくと良いかと。
・ 5e2 →5×(10の2乗) → 500
・ 0o、0b、0xはそれぞれ2進数、8進数、16進数を表す。
ex.0o11 → 3、0b11 → 9、0x11 → 17
・16進数では10以降をa~fで表すことができる(1~9そのまま 10=a,11=b...16=f)
ex.1a → 26、ff → 255

Number()

構文

Number(value)

説明

①文字列を数値にして返す。
②戻り値は10進数になる。
③小数点や10のn乗を表すe 基数を表す0x,0b,0oなども扱える
④+と-も扱える。
⑤上記したものと数字以外を含む文字列を引数にするとNaNを返す。
⑥戻り値は10進数になる。
⑦‘ ‘やnullは0の戻り値は0になる。
⑧Infinityも扱える

実行結果

qiita.rb
Number('123')    //123 (①)
Number('12.3')   //12.3  (③)
Number('12.00')  //12 (③ .00は省略されている)
Number('123e-1') //12.3(③と④e-1は10のマイナス一乗の意味) 
Number(' ')  //0(⑦)
Number(null) //0 (⑦)
Number('0x11')  //17(③‘0x’は16進数を表す表記。0x11で16進数の11の意味) 
Number('0b11')  //3(③‘0b’は2進数を表す表記。0b11で2進数の11の意味) 
Number('0o11')  //9(③‘0o’は8進数を表す表記。0o11で8進数の11の意味) 
Number('foo')   //NaN(⑤ちなみにNaNは数値型です。) 
Number('100a')  //NaN (⑤)
Number('-Infinity') //-Infinity (④と⑧)

parseInt()

構文

parseInt(string[, radix])
(radixは基数の意味。2進数とか16進数とか。)

説明

第一引数(string)を文字列に変換し解析して、整数またはNaNを返す。
①数字以外の文字に出会うと、それ以降は無視し、その時点までで解析した整数を返す。
②したがって、少数の場合は.以下を切り捨てた値を返す。
③ただし+と-は扱える。
④第二引数(radix)で変換時の基数をしていすることができる。
⑤省略した場合は10進数として変換するが注意が必要。基本的には基数を指定した方が良い。(※理由)

※0で始まる数値の場合に不具合や予期せぬ結果になりやすいため。
※0x、0b、0oは16進数、2進数、8進数を表すため、これらで始まる値はNaNではなく10進数での数値として返ってくる)ex. parseInt(0o11) // 9(0oは8進数を表す)
※上記以外でも0で始まる数値は8進数か10進数のどちらかで返されてしまうが、それは環境による。

実行結果

qiita.rb
parseInt('123',10)  //123 
parseInt('12.3',10) //12 (②小数点以下は切り捨てる)
parseInt('ff', 16)  //255(③16進数の整数)
parseInt('123px',10) //123(①pxは無視される)
parseInt('-123',10)  //-123(③)
parseInt('Infinity',10) //NaN(Infinityは扱えない)

parseFloat()

構文

parseFloat(value)
valueは文字列か数値

説明

①引数(value)を実数に変換する。(実数とは存在する全ての数。対義語は虚数)
②引数が数値の場合は数値を返す。
③引数が文字列の場合は文字列を数値として解析し、その結果を返す。

④したがって小数点も扱える。10のn乗を表す’e’も扱える。
⑤Infinityも扱える。
⑥+と-も扱える。
⑦解析できない数値は無視される。
⑧parseIntとほとんど変わらないが、基数がないので10進数で返す。

実行結果

qiita.rb
parseFloat('2.8') //2.8(④小数点も扱える)
parseFloat('73bpm') //73(⑦解析できない文字は無視)
parseFloat('280e-2') //2.8 (④と⑥280×(10の-2乗)なので)
parseFloat({toString: function(){ return 2.8}}) //2.8
parseFloat('ff') //NaN(⑥10進数で返すため)
parseFloat('Infinity') //Infinity(⑤)

参考

Number - JavaScript | MDN
parseInt() - JavaScript | MDN
parseFloat() - JavaScript | MDN

追記

指摘をいただき下記をそれぞれに加えました。
・+と-が扱えるか否か
・Infinityが扱えるか否か

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

Javascript でランダムな正の整数を取得

Javascript でランダムな正の整数を取得

sample.js
// 0〜100のランダムな数字を取得する
var rand = Math.floor(Math.random() * 101);

Math.random()
0以上1未満の乱数を取得できます。
例) 0.7422

Math.floor()
これを用いることで小数点以下を切り捨てた数値を取得できます。
例) Math.floor(81.6398) → 81

複雑に感じますが簡単に言うと

この2つを組み合わせることで「ランダムな正の整数を取得できる」ことを覚えておきます。

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

Javascript でランダムな正の整数を取得、(後半はくじ引きプログラム)

Javascript でランダムな正の整数を取得

sample.js
// 0〜100のランダムな数字を取得する
var rand = Math.floor(Math.random() * 101);

Math.random()
0以上1未満の乱数を取得できます。
例) 0.7422

Math.floor()
これを用いることで小数点以下を切り捨てた数値を取得できます。
例) Math.floor(81.6398) → 81

複雑に感じますが簡単に言うと

この2つを組み合わせることで「ランダムな正の整数を取得できる」ことを覚えておきます。

くじ引きプログラム

以上をふまえて「ifとelse」を使い、
0〜1のランダムな数字を取得し、
1の場合は当たりと表示
それ以外(今回の場合は事実上0の場合)は外れと表示させるプログラムを書いてみます。

sample.js
// 0〜1のランダムな数字を取得
var rand = Math.floor(Math.random() * 2);

// 1の場合
if (rand === 1) {
   document.write('当たり');
// 1以外の場合
} else {
   document.write('外れ');
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jQueryでパスワードの表示と非表示を切り替える

はじめに

railsでアプリケーションを行っている初学者のいりふねです。
今回、ユーザー登録画面のパスワード入力欄の表示と非表示を切り替える実装を行いました。ネット上に「カンタン」と書かれた同記事が多くありましたが、自分の環境で実装するのは少し時間がかかりました。
そこで、アウトプット兼自分用メモとして私の環境でうまく動作した手順を書いていきます。

開発環境

  • Rails 5.2.4.3
  • Haml 5.1.2
  • jquery-rails (4.4.0)
  • form_forを使用して実装しています

実現すること

ユーザー登録画面などでデフォルトで非表示になっているパスワードをcheck_boxを使用して表示と非表示を切り替えます。具体的には、以下Gifのような状態を目指します。
e177a528d227549c89344e4620d8b441.gif

手順

  1. check_boxの設置
  2. password_fieldにidを付与
  3. jsファイルにjQueryを記述

check_boxの設置

hamlファイルにcheck_boxを記述します。今回は、説明に不要なclass名などは予め外した状態にしています。cssなどをつけるときは別途設定して下さい。

example.html.haml
= form_for @user, url: user_registration_path do |f|

〜中略、password_fieldの記述などがあります〜

    %p
      = check_box_tag :passcheck
      = f.label "パスワードを表示する"

順番に説明します。
まず、pタグで囲ったのは、チェックボックスや「パスワードを表示する」という文字が、直上のpassword_fieldの右に回り込んでしまうためです。cssを編集することでカバーも可能でしょうが、pタグだけで解決するので、今回はこの方法を採用しました。

次に「check_box_tag」の部分です。これは、関連するモデルがない時のチェックボックスの書き方になります。他のフォームの記述に揃えて「= f.check_box」を使用すると、引数として保存先モデルのカラム名の記述が必須となってしまいます。今回のチェックボックスはレコードの保存が目的ではないので、「check_box_tag」を使用しました。

参考にさせていただいた記事【初心者向け】チェックボックスの書き方あれこれ[Ruby][Rails]

同行後半の「:passcheck」は、nameとidを指定しています。検証ツールで確認すると反映されていることが分かります。idはこの後、jQueryで要素を指定するための使用します。
スクリーンショット 2020-05-23 8.39.00.png

password_fieldにidを付与

password_fieldにjQueryで要素を指定するためのidを付与します。コードの後半部分がidを付与している箇所となります。

example.html.haml
〜省略〜
= f.password_field :password, autocomplete: "off", placeholder: "7文字以上の半角英数字", id: "js-password"
〜省略〜
= f.password_field :password_confirmation, autocomplete: "off", placeholder: "もう一度入力して下さい", id: "js-password_confirmation"
〜省略〜

ここで、idを同じものにしてしまうと、検証ツールのコンソール上で「idが重複しているよ!!」と黄色で警告が出てきます。ページが表示されないほどのエラーにはなりませんが、気持ち良いものではないので、今回はid名を分けて書きました。

jsファイルにjQueryを記述

jsファイルを作成し、コードを記述します。

example.js
$(function() {
  $('#passcheck').change(function(){
    if ($(this).prop('checked')) {
      $('#js-password').attr('type','text');
      $('#js-password_confirmation').attr('type','text');
    } else {
      $('#js-password').attr('type','password');
      $('#js-password_confirmation').attr('type','password');
    }
  });
});

実は、jQueryはまだ勉強中なので、十分説明ができません。汗
参考にされていただいた記事パスワード表示時にマスキング有無を選択できるようにする方法

とはいえ、自分なりに言語化してみます。
まずは、先程チェックボックスに指定したid名#passcheckで要素を取得し、changeイベントを設定します。ちなみにイベントはclickに変えても動きました。

次にif文を記述し、取得した要素からcheckedプロパティの有無をpropメソッドで確認します。チェックされていれば真、それ以外なら偽という扱いになります。

if結果が真の場合、つまりチェックが入った状態であれば、フォームに入力されたパスワードの値に対して、attrメソッドを使用して属性を取り出します。チェック済みならtype属性のtextを表示、そうでなければtype属性のpasswordを表示で隠すという具合です。

今日の積み上げ

改めて、jQueryの復習が必要であると感じました。
と同時に未熟な私でも機能実装できるようにわかりやすくまとめられた記事の多さに感謝しました。今後もアウトプットを続けて同じ初学者の方のためになればと思います。

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

JScriptで将棋のUSIプロトコルに挑戦

はじめに

JScriptで将棋のUSIプロトコルに挑戦します。→PHP版はこちら
将棋ソフトは各自用意してください。

USIプロトコルの例

USIプロトコルの例です。これをJScriptで書いていきます。

将棋ソフトと通信を開始し、初手76歩とした局面を1秒間検討し、評価値や読み筋を取得するのが目的です。
>で始まる行はコマンド送信、それ以外は受信となります。

>usi
id name NNUE-v1.0 64SSE2 (based on YaneuraOu 2018 Otafuku 4.82)
id author by yaneurao, extended by ynasu87
option name Threads type spin default 4 min 1 max 512
option name Hash type spin default 16 min 1 max 1048576
option name MultiPV type spin default 1 min 1 max 800
option name WriteDebugLog type check default false
option name NetworkDelay type spin default 120 min 0 max 10000
option name NetworkDelay2 type spin default 1120 min 0 max 10000
option name MinimumThinkingTime type spin default 2000 min 1000 max 100000
option name SlowMover type spin default 100 min 1 max 1000
option name MaxMovesToDraw type spin default 0 min 0 max 100000
option name DepthLimit type spin default 0 min 0 max 2147483647
option name NodesLimit type spin default 0 min 0 max 9223372036854775807
option name Contempt type spin default 2 min -30000 max 30000
option name ContemptFromBlack type check default false
option name EnteringKingRule type combo default CSARule27 var NoEnteringKing var CSARule24 var CSARule27 var TryRule
option name EvalDir type string default eval
option name SkipLoadingEval type check default false
option name NarrowBook type check default false
option name BookMoves type spin default 16 min 0 max 10000
option name BookIgnoreRate type spin default 0 min 0 max 100
option name BookFile type combo default standard_book.db var no_book var standard_book.db var yaneura_book1.db var yaneura_book2.db var yaneura_book3.db var yaneura_book4.db var user_book1.db var user_book2.db var user_book3.db var book.bin
option name BookEvalDiff type spin default 30 min 0 max 99999
option name BookEvalBlackLimit type spin default 0 min -99999 max 99999
option name BookEvalWhiteLimit type spin default -140 min -99999 max 99999
option name BookDepthLimit type spin default 16 min 0 max 99999
option name BookOnTheFly type check default false
option name ConsiderBookMoveCount type check default false
option name PvInterval type spin default 300 min 0 max 100000
option name ResignValue type spin default 99999 min 0 max 99999
option name nodestime type spin default 0 min 0 max 99999
option name Param1 type spin default 0 min 0 max 100000
option name Param2 type spin default 0 min 0 max 100000
option name EvalSaveDir type string default evalsave
option name ConsiderationMode type check default false
option name OutputFailLHPV type check default true
usiok

>isready
readyok

>usinewgame
>position startpos moves 7g7f
>go byoyomi 1100
info depth 1 seldepth 1 score cp -84 nodes 82 nps 41000 time 2 pv 8c8d
info depth 2 seldepth 2 score cp -60 nodes 121 nps 40333 time 3 pv 8c8d 2g2f
info depth 10 seldepth 12 score cp -41 nodes 41683 nps 150480 time 277 pv 3c3d 2g2f 4a3b 5i6h 5a4b 4i5h 8c8d 6i7h 2b8h+
bestmove 3c3d ponder 2g2f

>quit

infoで始まる行にて、評価値や読み筋を取得できます。
score cpに続く数字が評価値、pvに続く文字列が読み筋です。

JScriptでの記述例

JScriptの良い点はWindows標準で動くことです。
ただし、次のコードはDOS窓が表示されるという欠点があります。(Windowsの仕様)

var usi = WScript.CreateObject('WScript.Shell').Exec('YaneuraOu.exe')

usi.StdIn.WriteLine('usi')

while(1){
    var line = usi.StdOut.ReadLine()
    if(line.match(/^usiok/)){
        break;
    }
}

usi.StdIn.WriteLine('isready')
usi.StdOut.ReadLine()

usi.StdIn.WriteLine('usinewgame')
usi.StdIn.WriteLine('position startpos moves 7g7f')
usi.StdIn.WriteLine('go byoyomi 1100')

while(1){
    var line = usi.StdOut.ReadLine()
    WScript.Echo(line)
    if(line.match(/^bestmove/)){
        break;
    }
}

usi.StdIn.WriteLine('quit')

続けて2手目(34歩)を検討するには、quitの前に次のようなコマンドを送信します。
指し手を2つ書くだけなので簡単です。

>position startpos moves 7g7f 3c3d
>go byoyomi 1100

7g7fなどの指し手の表記方法については、以下の説明と参考サイトを参考にしていください。

筋に関しては1から9までの数字で表記され、段に関してはaからiまでのアルファベット(1段目がa、2段目がb、・・・、9段目がi)というように表記されます。
位置の表記は、この2つを組み合わせます。5一なら5a、1九なら1iとなります。
そして、指し手に関しては、駒の移動元の位置と移動先の位置を並べて書きます。7七の駒が7六に移動したのであれば、7g7fと表記します。(駒の種類を表記する必要はありません。)
駒が成るときは、最後に+を追加します。8八の駒が2二に移動して成るなら8h2b+と表記します。
持ち駒を打つときは、最初に駒の種類を大文字で書き、それに*を追加し、さらに打った場所を追加します。金を5二に打つ場合はG*5bとなります

参考サイト

http://shogidokoro.starfree.jp/usi.html
https://qiita.com/tag1216/items/aa4ddc9354c83c66d1b4
https://github.com/yaneurao/YaneuraOu/blob/master/docs/USI%E6%8B%A1%E5%BC%B5%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89.txt

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

DropdownのUIを実装

はじめに

JavaScriptのフレームワークやライブラリを使わずに JavaScript、HTML、そしてCSSでドロップダウンUIを実装 したので、そのメモです。

ちなみにスタイル用途として、CSSフレームワークbulmaとWebアイコンのfont-awesomeを利用しています。

DropwdownのUI動作確認

実際のDropdownのUIは以下のCodepensより確認できます。

See the Pen OJyqPmJ by shinji uyama (@ushinji_0612) on CodePen.

解説

HTML

まず初めにDropwDownのHTMLの解説です。今回のDropwdownのHTMLコードは、bulmaのコード例を参考にしています。
https://versions.bulma.io/0.7.1/documentation/components/dropdown/

今回のDropdownのHTMLを簡略化すると以下の要素になります。

<!-- Dropwdown全体 -->
<div class="dropdown is-active">
  <div class="dropdown-trigger">
    <button>
      <!-- ボタンの開閉ボタン -->
    </button>
  </div>
  <div class="dropdown-menu" id="dropdown-menu" role="menu">
    <!-- ドロップダウンのMenu -->
  </div>
</div>

DropdownのMenuの表示/非表示は <div class="dropdown">に当てられているis-activeの有無で管理します。

また、Dropdownの開閉UIは<div class="dropdown-trigger">配下のbuttonによって行います。Buttonクリックが行われた際はis-activeを追加 or is-activeを削除することで、開閉を実現しています。

CSS

次にbulmaのCSSクラスの中で、Dropdownの開閉を行うis-activeの挙動を解説します。

まず初めに開閉対象であるMenuのCSSクラス.dropdown-menuを見るとdisplay: none;が当てられています。そのため、デフォルトではブラウザ上では表示させないようにできます。

一方で、is-activeが追加することで.dropdown-menuに対してdisplay: blockを当たるため、.dropdown-menu要素が表示されます。

<!--  コードを一部省略しています -->

.dropdown
  &.is-active
    .dropdown-menu
      display: block

.dropdown-menu
  display: none

CSSS詳細を知りたい方は、以下のリンクよりbulmaの該当コードを確認ください。
https://github.com/jgthms/bulma/blob/9a28ea17876715d00d0a8a59b9fdabfee967e56b/sass/components/dropdown.sass#L20

JavaScript

次にDropwdownの開閉を制御するJavaScriptについての解説です。

以下のコードがDropdownを開くコードになります。

document.addEventListener('DOMContentLoaded', function() {
  // 1. DOMが読み込まれた際に`.dropdown-trigger`のClassを持つHTMLElementを検索
  var nodelist = document.querySelectorAll('.dropdown-trigger');
  var elements = Array.prototype.slice.call(nodelist, 0);

  elements.forEach(function(element) {
    // 2. Dropdownの開閉ボタンを取得と、開閉を管理するDropdownのElementを取得
    var button = element.querySelector('button');
    var dropdown = element.parentNode;

    // 3. Dropdownの開閉ボタンがクリックされた際に、`is-active`クラスを追加するイベント追加
    button.addEventListener('click', function() {
      dropdown.classList.add('is-active');
    });
  });
});

処理の流れとしては、DOMがマウントされた際にDropdownに関連するHTMLに対してクリックイベントを登録することで、DropdownUIを実現させています。

具体的にはDropdownのTriggerとなるButtonがクリックされた際に<div class="dropdown">.is-activeを当てることで、DropwdownのMenuを表示させています。

次に Dropdownを閉じるUIの処理 について説明します。

そもそもDropdownを閉じたいケースを考えると、以下の2つが考えられます。
1. DropdownのMenu項目がクリックされた場合
2. Dropdown以外の範囲がクリックされた場合

この場合をどうやって検知する一番簡単な方法は、TriggerであるButtonのfocusが外れた場合、つまりblurを検知 すれば良いです。

具体的には以下のコードを追記すれば大丈夫です。

document.addEventListener('DOMContentLoaded', function() {
  var nodelist = document.querySelectorAll('.dropdown-trigger');
  var elements = Array.prototype.slice.call(nodelist, 0);

  elements.forEach(function(element) {
    var button = element.querySelector('button');
    var dropdown = element.parentNode;

    button.addEventListener('click', function() {
      dropdown.classList.add('is-active');
    });

// 【追記】 Dropdownを閉じるコード追記
+    button.addEventListener('blur', function() {
+      dropdown.classList.remove('is-active');
+    });
  });
});

最後に

今回自前でDropdownの開閉UIを実装することで、すごく勉強になりました。

特に、閉じる場合のUIについて、当初は「閉じる際のクリック時の位置をDropdownのHTML範囲であるか調べて、範囲外である場合閉じる」みたいな方法を考えていました。そのため、blurを使えばシンプルに制御する方法ははとても目から鱗でした。

(補足) focusが外れた際にDropdownを閉じる実装の場合、キーボード操作ではMenuを選択する前にDropdownMenuが消えてしまいます。そのため、より良いアクセシビリティを考えると、別の方法考える必要がありますね。

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

Vue.js 親・子コンポーネント間 データ 受け渡し 

やりたいこと

Vue.js (Vue CLI)で、親・子コンポーネント間でデータを受け渡したい。

今回は以下のような流れでデータを受け渡します。
child1.vue(子1) → pearent.vue(親) → child2.vue(子2)

child1.vue(子1) → pearent.vue(親)、子から親へデータを移動

今回は、ボタンを押すとinputに記載された文字列を親コンポーネントに渡す処理を実装します。

まずchild1.vue(子1)

<template>
    <div id="target-input">
        <p>以下input内に入力されたデータを親に渡します</p>
        <input type="text" @input="example = $event.target.value">
        <button @click="sendToParent">親へデータを渡す</button>
    </div>
</template>

<script>
export default {
    data(){
        return {
            example : "",
        }
    },
    methods: {
        sendToParent() {
             this.$emit("textForParent",this.example);
        }
    },
};
</script>

ポイント
methods内、this.$emitは引数を2つとります。1つ目は好きに名前を付けてください。
 (後ほど親コンポーネント内で使用します)
・2つ目は、親コンポーネントへ渡したいデータを指定します。今回はdata内のexampleです。

上記で「送り口」の実装は完成です。

しかしこのままでは「受け取り口」が用意されていない為、データが親コンポーネントへ渡りません。
受け取り側であるpearent.vue(親)に、「受け取り口」を実装していきます。
pearent.vue(親)

<template>
  <div id="app">
    <child1 v-on:textForParent="text = $event"></child1>
    <child2></child2>
  </div>
</template>

<script>
import child1 from './child1.vue'
import child2 from './child2.vue'

export default {
  name: 'App',
  components: {
    child1,
    child2,
  },
  data(){
    return {
      text : "",
    }
  },
};
</script>

ポイント
v-onディレクティブの後に、子コンポーネント内で設定したthis.$emitの第1引数をとります。
$eventには、子コンポーネント内のthis.$emitの第2引数である、データが入ります。

これで、親コンポーネント data内のtextへ、子コンポーネントからデータを渡すことが出来ました。

pearent.vue(親) → child2.vue(子2) 、親から子へデータを移動

では今度は親コンポーネントから子コンポーネントへデータを移動してみましょう。
先ほどのtextを、child2.vueへ渡してみます。

pearent.vue(親)

<template>
  <div id="app">
    <child1 v-on:textForParent="text = $event"></child1>
    <child2 v-bind:textForChild2="text"></child2>
  </div>
</template>

<script>
import child1 from './child1.vue'
import child2 from './child2.vue'

export default {
  name: 'App',
  components: {
    child1,
    child2,
  },
  data(){
    return {
      text : "",  //実際には、child1からのデータが入っています
    }
  },
};
</script>

ポイント
<child2></child2>v-bindを付けてあげます。textのデータを子に送ります。
上記で「送り口」はできました。

今度はchild2.vue(子2)に「受け取り口」を作ります。
child2.vue(子2)

<template>
    <div>
        <p>以下に親コンポーネントから渡されたtextデータを表示します。</p>
        <p>{{textForChild2}}</p>
    </div>
</template>>

<script>
export default {
    props:["textForChild2"],
};
</script>

ポイント
propsを設定して親コンポーネントからデータを受け取ります。これをtemplate内で呼び出します。

以上です!
今回の例ではchild1.vue(子1)内で作られたデータがpearent.vue(親)へ渡り、そこからchild2.vue(子2)へ渡りました。

子から親へ、親から子へデータを受け渡しすることが出来ました!

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

Vue.js 親・子 コンポーネント間 データ 受け渡し 

やりたいこと

Vue.js (Vue CLI)で、親・子コンポーネント間でデータを受け渡したい。

今回は以下のような流れでデータを受け渡します。
child1.vue(子1) → pearent.vue(親) → child2.vue(子2)

child1.vue(子1) → pearent.vue(親)、子から親へデータを移動

今回は、ボタンを押すとinputに記載された文字列を親コンポーネントに渡す処理を実装します。

まずchild1.vue(子1)

<template>
    <div id="target-input">
        <p>以下input内に入力されたデータを親に渡します</p>
        <input type="text" @input="example = $event.target.value">
        <button @click="sendToParent">親へデータを渡す</button>
    </div>
</template>

<script>
export default {
    data(){
        return {
            example : "",
        }
    },
    methods: {
        sendToParent() {
             this.$emit("textForParent",this.example);
        }
    },
};
</script>

ポイント
methods内、this.$emitは引数を2つとります。1つ目は好きに名前を付けてください。
 (後ほど親コンポーネント内で使用します)
・2つ目は、親コンポーネントへ渡したいデータを指定します。今回はdata内のexampleです。

上記で「送り口」の実装は完成です。

しかしこのままでは「受け取り口」が用意されていない為、データが親コンポーネントへ渡りません。
受け取り側であるpearent.vue(親)に、「受け取り口」を実装していきます。
pearent.vue(親)

<template>
  <div id="app">
    <child1 v-on:textForParent="text = $event"></child1>
    <child2></child2>
  </div>
</template>

<script>
import child1 from './child1.vue'
import child2 from './child2.vue'

export default {
  name: 'App',
  components: {
    child1,
    child2,
  },
  data(){
    return {
      text : "",
    }
  },
};
</script>

ポイント
v-onディレクティブの後に、子コンポーネント内で設定したthis.$emitの第1引数をとります。
$eventには、子コンポーネント内のthis.$emitの第2引数である、データが入ります。

これで、親コンポーネント data内のtextへ、子コンポーネントからデータを渡すことが出来ました。

pearent.vue(親) → child2.vue(子2) 、親から子へデータを移動

では今度は親コンポーネントから子コンポーネントへデータを移動してみましょう。
先ほどのtextを、child2.vueへ渡してみます。

pearent.vue(親)

<template>
  <div id="app">
    <child1 v-on:textForParent="text = $event"></child1>
    <child2 v-bind:textForChild2="text"></child2>
  </div>
</template>

<script>
import child1 from './child1.vue'
import child2 from './child2.vue'

export default {
  name: 'App',
  components: {
    child1,
    child2,
  },
  data(){
    return {
      text : "",  //実際には、child1からのデータが入っています
    }
  },
};
</script>

ポイント
<child2></child2>v-bindを付けてあげます。textのデータを子に送ります。
上記で「送り口」はできました。

今度はchild2.vue(子2)に「受け取り口」を作ります。
child2.vue(子2)

<template>
    <div>
        <p>以下に親コンポーネントから渡されたtextデータを表示します。</p>
        <p>{{textForChild2}}</p>
    </div>
</template>>

<script>
export default {
    props:["textForChild2"],
};
</script>

ポイント
propsを設定して親コンポーネントからデータを受け取ります。これをtemplate内で呼び出します。

以上です!
今回の例ではchild1.vue(子1)内で作られたデータがpearent.vue(親)へ渡り、そこからchild2.vue(子2)へ渡りました。

子から親へ、親から子へデータを受け渡しすることが出来ました!

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

Kotlin/JSを触ってみる

追記

も少し触ってできたことがあったので書き足しました。

最近になりJavaScriptを初めて触れ、試してみたかったけれど、なかなか手を付けられずに
いたKotlin/JSをリファレンスを見ながら試してみました。
https://kotlinlang.org/docs/tutorials/javascript/running-kotlin-js.html
https://kotlinlang.org/docs/tutorials/javascript/using-packages-from-npm.html

Kotlin/JS とは

簡単に言ってしまうと、Kotlin/JVMで書いていたコードをJavaScriptに変換してくれるといったことをしてくれます。
ただし変換してくれるのはあくまでKotlinが対応しているモジュールだけであって、JDKや
一般的なサードパーティー製のJavaライブラリ、Springなどのフレームワークは対象外とのことです。

詳しくは公式リファレンスの概要にて。
https://kotlinlang.org/docs/reference/js-overview.html

早速触ってみる

私は「Kotlin/JS for browser」を選択しました。
2020-05-21.png
プロジェクト名は「sample-js」で行きます。

Gradleの中身です。

build.gradle.kts
plugins {
    id("org.jetbrains.kotlin.js") version "1.3.72"
}

group = "org.example"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

dependencies {
    implementation(kotlin("stdlib-js"))
}

kotlin.target.browser { }

プロジェクトのフォルダ階層は一般的なGradleプロジェクトと変わらないですね。
2020-05-21 (1).png

ブラウザのコンソール上に「Hello, World!」

それでは「src\main\kotlin」にいつも書いているようなKotlinのコードを書いていきます。

App.kt
fun main() {
    println("Hello, World!")
}

Node.jsであればこのまま実行すればいいのですが、今回はbrowserなので
「src\main\resources」にhtmlを用意します。

index.html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Kotlin/JS</title>
    </head>
    <body>
        <h1>これはKotlin</h1>
    </body>
    <script src="sample-js.js"></script>
</html>

ここで見ていただきたいのはscriptタグ内のソース名です。
実はKotlin/JSで生成されるJavaScriptのモジュールはアーティファクトの名前がそのまんま使われます。
「kotlin-for-javascript」とプロジェクト名を付ければ「kotlin-for-javascript.js」となります。
(何故かtype宣言をするとコンソール上にエラーが出てしまう。純粋なJavaScriptではないから?)

ようやく準備が出来たので実行してみましょう。
gradleツールウインドウからrunを実行します。

するとブラウザが立ち上がりhtmlが表示されます。
結果を確認したいので「開発者モード→Console」へ
2020-05-21 (3).png
きちんと実行がされていました。

ちなみに引数に「--continuous」を付けるとコンパイルを自動的に実行してくれます。

DOMの操作。

document.bgColor = "FF33AA"

2020-05-23.png
色は特に意味なく値を入れましたが、鮮やかなピンクが背景色になりました。
ここも触れるようになりたいですね。

入力した値も取り出せるようです。

<input type="text" id="name">
<input type="button" id="output" value="コンソールに表示">
val button = document.getElementById("output") as HTMLInputElement

    button?.addEventListener("click", {
        val name = (document.getElementById("name") as HTMLInputElement).value
        println(name)
    })

2020-05-23 (1).png
試行錯誤しながら書いていたのでこの記述であっているかは定かではありませんが...
JS側のリファレンスと対応関係関係がほぼ同じみたいなので
わからなくなったらmozillaのリファレンス見ればなんとかなりそうと感じました。

npmパッケージのライブラリを使用してみる

KotlinからJavaScriptのパッケージを呼び出しせるようです。
JavaScriptに関する知識が皆無のため、リファレンスの実行例を実際にやってみます。

Kotlin DSLに使用したいパッケージを宣言

build.gradle.kts
implementation(npm("is-sorted"))

is-sortedというパッケージはソートしているかを判別し、真理値を返すプログラムです。

次にkotlin側で外部宣言というものをしてあげます。

is-sorted.kt
@JsModule("is-sorted")
@JsNonModule
external fun <T> sorted(a: Array<T>): Boolean

この外部宣言はJavaScriptん動的型付けの変数とKotlinの静的な型付けを
接続する為に使用される宣言だそうです。
@JsModuleは使用するパッケージのインポート宣言、
@JsNonModuleはJavaScriptのモジュールではないという意味を示しております。
どちらも外部宣言をする際には必要とのこと。

それではmain関数に追記していきます。

App.kt
fun main() {
    println("Hello, World!")
    println(sorted(arrayOf(1, 2, 3)))
    println(sorted(arrayOf(3, 1, 2)))
}

これで実行をすると、
2020-05-21 (6).png

結果が表示されました。
最初の比較はきちんと昇順なのでtrue
もう一つは並びがバラバラなのでfalseが表示されています。

おわり

まだ全然基礎レベルでしか触っていないですが、普段使っている言語で色んなものが書けるっていいですね...

まだまだkotlinは勉強中でhtmlやJavaScriptなどもまともに扱えていない状態なので
もう少し勉強をしてから続きを書きたいと思います。

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