20210901のJavaScriptに関する記事は16件です。

【JavaScript】重複しない整数乱数を生成する関数

参考 JavaScriptで重複しない乱数を生成する…関数化されていない重複しない整数乱数の生成方法 やりたいこと 重複しない整数乱数を配列で生成したい + 関数として利用したい やったこと 重複しない整数乱数を生成する関数を参考URLのコードをちょっと改変して作成 コード Google Apps Script環境で実装しているためJavaScriptだと修正が必要かも。 改変前の関数化していないコード randomizing.js function randomizing(){ var arr = []; numArr = []; for(var i=0; i < 5; i++){ arr[i]=i+1; } for(var j = 0,len = arr.length; j < 5; j++, len--) { rndNum = Math.floor(Math.random()*len); numArr.push(arr[rndNum]); arr[rndNum] = arr[len-1]; } //debug for(var k=0; k<numArr.length; k++){ console.log(numArr[k]) } } 実行結果 4 1 2 5 3 関数化したコード generateRandomNumArray.js //test用 function test(){ generateRandomNumArray(30,5); } /** * @param {number} maxNum - 取りうる最大値 * @param {number} generateArrayLength - 生成する配列の長さ * @return {number[]} */ function generateRandomNumArray(maxNum, generateArrayLength){ let generateArray = []; //ランダム格納用配列 let numberArray = []; //ランダム生成用配列 //ランダム生成用配列を作成 for(let i=0; i<maxNum; i++){ numberArray[i] = i+1; } //ランダム格納用配列にランダム整数を格納 for(let j=0,len=numberArray.length; j<generateArrayLength; j++,len--){ let rndNum = Math.floor(Math.random()*len); generateArray.push(numberArray[rndNum]); numberArray[rndNum] = numberArray[len-1]; } //debug for(let k=0; k<generateArray.length; k++){ console.log(generateArray[k]); } return generateArray; }//end function 実行結果 19 25 17 29 30 あとがき GAS+SSでランダムな規定数の行にランダムチェック的なフラグをたてたかったのだが、重複しないような乱数配列生成をしてくれるようなジャストな関数はなかった。 もしかしたらもっと短く済む方法があるかもしれない。 あと正直コードの意味がわからない、コピペでも動いてしまうって怖いね
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptのオブジェクト配列を特定キーでuniq処理する

オブジェクト配列を特定キーの値でユニーク抽出する必要があったので調べてみたのですが、シンプルな記述が見あたらなかったので書いてみました。 Array.mapで値とオブジェクトのArrayイテレータを作り、 new Map(イテレータ)で値でuniq化し、 Map.values() で オブジェクトのイテレータを作り、 [...イテレータ] でArrayにする オブジェクトのcキーでuniq処理する例: const array = [ { a: 0, b: 1, c: 2 }, { a: 3, b: 4, c: 2 }, { a: 5, b: 6, c: 7 }, { a: 8, b: 9, c: 7 }, ]; let uniq_by_c = [...new Map(array.map(obj => [obj.c, obj])).values()] console.log(uniq_by_c); 実行結果 [ { a: 3, b: 4, c: 2 }, { a: 8, b: 9, c: 7 } ]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptのオブジェクト配列を特定キーの値でuniq処理する

オブジェクト配列を特定キーの値でユニーク抽出する必要があったので調べてみたのですが、シンプルな記述が見あたらなかったので書いてみました。 Array.mapで値とオブジェクトのArrayイテレータを作り、 new Map(イテレータ)で値でuniq化し、 Map.values() で オブジェクトのイテレータを作り、 [...イテレータ] でArrayにする オブジェクトのcキーの値でuniq処理する例: const array = [ { a: 0, b: 1, c: 2 }, { a: 3, b: 4, c: 2 }, { a: 5, b: 6, c: 7 }, { a: 8, b: 9, c: 7 }, ]; let uniq_by_c = [...new Map(array.map(obj => [obj.c, obj])).values()]; console.log(uniq_by_c); 関数化 const uniq = (array, key) => [...new Map(array.map(obj => [obj[key], obj])).values()]; console.log(uniq(array, 'c')); 実行結果 [ { a: 3, b: 4, c: 2 }, { a: 8, b: 9, c: 7 } ]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

はじめに こんにちは。 JavaScriptの配列とオブジェクトの違いについてアウトプットしていきます! 配列とオブジェクトの違い 配列が「添字を使って格納されたデータを参照する」のに対して、オブジェクトは「名前(プロパティ)を使って格納されたデータを参照する」という点です。 参考:https://atmarkit.itmedia.co.jp/ait/articles/1011/29/news109_2.html 配列 JavaScript let vegetables = ['トマト', 'ナス','キュウリ']; console.log(vegetables[0]); // トマト 変数vegetablesの添字に0を指定して出力すると、参照した値「トマト」が出力される。 オブジェクト JavaScript let user = { name: '佐藤', age: 20 } console.log(user.age); // 20 変数userのプロパティageを指定して、20を出力することができる。 最後に ここまで配列とオブジェクトの違いについて簡単にまとめました。 こうやってまとめながら比較していくと覚えやすかったので、適宜アウトプットしながら学習を進めていきます!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[備忘録]フロント側の対応だけでNginxでキャッシュされた古い画像が表示されないようにする

Nginxで画像ファイルをキャッシュするようにしていたのですが、同じ名前で画像を更新した場合に古い画像がしばらく表示されてしまいます。 かといって、まれにしか更新されない画像のためにキャッシュを無効にするのもイケてないので、解決策を調べてみました。 結論 imgタグのsrcに/path/image.jpg?xxxxxのようにパスの後にランダムな値を指定する ※右クリックで「名前を付けて画像を保存」しても?以降は認識されません。 具体例 参考にした投稿だとランダム値を指定してましたが、それだと常にキャッシュがスルーされてしまうのでレコードの更新日時を指定することにしました。 サーバーから以下のようなテーブルのデータを取得して、imageUrlの後ろに更新日時を指定することで、同名のファイルで更新して画像ファイルのURLが同じだったとしても更新日時が異なるためキャッシュから読み込まれず、最新の画像を表示することができます。 論理名 物理名 型 id ID 画像URL imageUrl string 更新日時 updatedAt string test.vue <template> <div> <img :src="imageUrl" /> </div> </template> <script lang="ts"> item; get imageUrl() { // 更新時に新しい画像が表示されるようにするため、更新日時をファイル名の後ろに付与 return `${this.item.imageUrl}?${moment(this.item.updatedAt).format('YYYYMMDDHHmmss')}`; } ただ、これだと画像URL以外のカラムが更新された場合でも画像を読み直してしまうので、頻繁に更新するようなテーブルの場合は要検討ですね。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

すべての開発者へ。人気GitHubリポジトリ9選

本記事は、Simon Holdorf氏による「9 Popular GitHub Repos For Every Web Developer」(2021年4月4日公開)の和訳を、著者の許可を得て掲載しているものです。 すべての開発者へ。人気GitHubリポジトリ9選 便利なツール、参考になる例など... はじめに GitHubは、最近の(ウェブ)開発に関連するすべてのワンストップショップです。フレームワーク、デモ、あらゆる種類のコレクションなど、GitHubで見つけられないものはないでしょう。しかし、この膨大な量が問題です。あまりにも多くのレポジトリがあるので、おそらく聞いたことのないクールなものがあります。 そこで今回も、知っておくべき最も人気のGitHubリポジトリを紹介することにします。各リポジトリには少なくとも30,000個の星が付いています。 1. Realworld このリストの最初のリポジトリはRealworldです。制作者は「すべてのデモアプリの母」と呼んでいます。確かに大胆な発言ですが、誇張ではないと思います。 Realworldは、典型的なMedium.comクローンです。しかし、それだけではありません。リポジトリでは、さまざまなフロントエンドとバックエンドの実装を選ぶことができ、それらを自由に組み合わせることができます。 Vue.js + Node/ExpressやReact /Redux + Rust?どちらもできます! Realworldでは、まったく同じブログアプリが、ほぼすべての人気言語やフレームワークでどのように構築されているかを見ることができます。すごいですよね? 2. You Don't Know JS Yet このリポジトリは、Getifyという別名で広く知られているKyle Simpson氏の人気書籍シリーズです。この書籍では、JavaScriptの仕組みを深く掘り下げ、次のトピックを扱っています。 はじめに スコープとクロージャ オブジェクトとクラス 型と文法 同期と非同期 ES.Next以降 何よりも素晴らしいのは、何と言っても完全無料で読むことができることです!これは間違いなくJavaScriptに関する最高のシリーズの1つであり、JSの本質を理解するのにとても役立ちました。JavaScriptをよく知っていると思っている人も、この本を読んでみてください。驚くこと間違いなしです。 3. Airbnb JavaScriptスタイルガイド 最も妥当なJavaScriptへのアプローチです。 Airbnb JavaScriptスタイルガイドは、最も人気があり、よく使用されているスタイルガイドです。より良いJSコードを記述するのに役立ち、チームでの作業やESLintとの組み合わせで特に役立ちます。 varの代わりにconstを使用する例です。 4. Storybook Storybookは、UIコンポーネントの開発環境です。コンポーネントライブラリの参照、各コンポーネントのさまざまな状態の表示、コンポーネントのインタラクティブな開発とテストが可能です。React、Vue、Angular、React Native、Ember、ウェブコンポーネントなどをサポートしています! Storybookは、アプリ外で動作します。そのため、UIコンポーネントを分離して開発でき、コンポーネントの再利用やテストの容易性、開発速度を向上させることができます。アプリ固有の依存関係を気にすることなく、素早く構築できます。 こちらに素晴らしい例があります。 5. HTML5 Boilerplate HTML5 Boilerplateは、ウェブ上で最も人気のある、プロフェッショナルなフロントエンドテンプレートの1つです。高速で堅牢、かつ柔軟性に優れたウェブサイトやアプリの構築に役立ちます。Microsoft、NASA、Nikeなどの企業が使用しています。 主な機能は次の通りです。 HTML5対応 プログレッシブエンハンスメントを念頭に置いた設計 Normalize.css、jQuery、Modernizerを含む ウェブサイトのパフォーマンスとセキュリティを向上させる設定 CSSメディアクエリのプレースホルダ パフォーマンスを最適化したデフォルトの印刷スタイル Google Universal Analyticsスニペットの最適化バージョン 6. Node.js Best Practices このリポジトリは、Node.jsの開発に関するさまざまなベストプラクティスを包括的に集めたものです。現在、80を超えるベストプラクティス、スタイルガイド、アーキテクチャのヒントで構成されています。 次のようなベストプラクティスがあります。 プロジェクト構造 エラー処理 コードスタイル テストと全体的な品質 制作開始 セキュリティ パフォーマンス これは素晴らしいリポジトリであり、情報源です。Node.jsの開発に興味があれば、ぜひチェックしてください! 7. Front-End Checklist ウェブアプリを起動する前に、何が必要か、何のテストが必要か、自問したことはありますか?Front-End Checklistリポジトリに答えがあります! 掲載されている項目の多くは、大半のプロジェクトで必須となっており、次のような構成です。 Head HTML ウェブフォント CSS 画像 JavaScript セキュリティ パフォーマンス アクセシビリティ SEO 翻訳 Apple Web App MetaDataの例です。 8. NW.js NW.jsは、Chromiumとnode.jsベースのアプリランタイムです。NW.jsを使用して、HTMLとJavaScriptでネイティブアプリを作成できます。また、DOMから直接Node.jsモジュールを呼び出すことができ、あらゆるウェブ技術を使用してネイティブアプリを作成する新しい方法を可能にします。 主な機能は次の通りです。 最新のHTML5、CSS3、JS、WebGLで作成したアプリ Node.js APIとそのすべてのサードパーティモジュールの完全サポート 優れたパフォーマンス:NodeとWebKitは同じスレッドで実行 アプリのパッケージ化と配布が容易 Linux、Mac OS X、Windowsで利用可能 9. fullPage.js Alvaro Trigo氏のfullPage.jsは、SPA(シングルページアプリケーション)やランドスケープスライダーを作成できる、シンプルで使いやすいライブラリです。Vue、React、Angular対応で、モバイルデバイスにも完全に対応しています。 こちらに素晴らしい例があります。 このライブラリは、オープンソースプロジェクトでは無料で使用できます。ただし、商業環境で使用する場合は、ライセンスを購入する必要があります。 とてもクールなライブラリなので、ぜひチェックしてください! おわりに これらのリポジトリをあなたのプロジェクトやその他の目的に使ってもらえたら嬉しいです。 記事を気に入ったらTwitterでフォローしてください。プログラミング、制作、執筆、キャリアについてもっと学べます? 翻訳協力 この記事は以下の方々のご協力により公開する事ができました。改めて感謝致します。 Original Author: Simon Holdorf (@simonholdorf) Original Article: 9 Popular GitHub Repos For Every Web Developer Thank you for letting us share your knowledge! 選定担当: @gracen 翻訳担当: @gracen 監査担当: - 公開担当: @gracen ご意見・ご感想をお待ちしております 今回の記事はいかがでしたか? ・こういう記事が読みたい ・こういうところが良かった ・こうした方が良いのではないか などなど、率直なご意見を募集しております。 頂いたお声は、今後の記事の質向上に役立たせて頂きますので、お気軽に コメント欄にてご投稿ください。Twitterでもご意見を受け付けております。 皆様のメッセージをお待ちしております。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Temporalで8月32日をサポートする

Temporalとは、JavaScriptに組み込まれた新しい日付計算機能です(正確にはまだStage 3ですが、仕様自体はほとんど完成している状態です)。従来あったDateのさまざまなつらい部分が解消されたAPIとなっており、実用化が待たれます。 8月32日とは、9月1日の到来を拒む一部の人たちが主張する仮想上の日付です。日本では9月1日は夏休みが終わり学校に登校しなければならない日ですが、8月32日ではまだ夏休みが続いているとされています。 残念ながら、日付を扱う多くのアプリケーションでは8月32日がサポートされていません。しかし実は、TemporalではCalendarという仕組みを用いることで8月32日をサポートすることができまです。この記事では、8月32日をサポートするCalendar、すなわち永遠の夏休みカレンダーとして筆者が実装したeternal-summer-vacationの実装や使用例を紹介します。独自のCalendarを作りたい際の参考にしてください。 なお、まだTemporalは本物の実装が使えるわけではないので、このパッケージは公式Polyfillである@js-temporal/polyfillを使用しています。 サンプル こちらは、Reactで今年のカレンダーを表示するサンプルです。ボタンを押すと通常のカレンダーと永遠の夏休みカレンダーを切り替えることができます。コードを読んでみると、両者は「Temporal.PlainDateオブジェクトを作る際にどのCalendarオブジェクトを使うか」という点にのみ影響を与えており、その他の部分は全く同じコードで2種類のカレンダーを描画することができることが分かります(useCalendar.tsを参照)。このように、Temporalではカレンダーという概念が抽象化されており、うまくコードを書くことで異なるカレンダーに対応したコードを書くことができます1。 Calendarの役割 Temporalでは、日付の内部表現はISO 8601で定められた通りグレゴリオ暦です。Calendarは、その日付をどう表示するかを司るというのが基本的な役割です。さらに、付随して「月はいくつあるのか」「この月には何日あるのか」「一週間は何日なのか」といったことをカスタマイズできるようになっています。 今回実装したカレンダーの中心となるのは、例えばISO 8601カレンダーで2021-09-01と表現された日付を「2021年8月32日」に変換する、あるいはその逆を行うロジックです。 独自のCalendarを作るには、Temporal.Calendarを継承するのが簡単です。以下ではコードの一部を抜き出しながら説明します。全体は以下のURLで見ることができます。 https://github.com/uhyo/eternal-summer-vacation/blob/8f3927d88f2440c33c70dbba6f4dc49086d1df1a/src/eternal-calendar.ts 永遠の夏休み日付の計算 ある日付が永遠の夏休みカレンダー上で何月何日になるのか定義するには、Calendarのmonthメソッドやdayメソッドをオーバーライドします。 override month(date: DateLike) { return eternalDate(getPlainDateWithISOCalendar(date))[0]; } override day(date: DateLike) { return eternalDate(getPlainDateWithISOCalendar(date))[1]; } 実装に見えるeternalDateが、ISO 8601日付を永遠の夏休み日付に変換する関数です。その前段にあるgetPlainDateWithISOCalendarは、与えられた日付のISO 8601日付を得るための関数です。というのも、ここで与えられるdate引数はTemporal.PlainDateオブジェクト(Temporalで日付を表すオブジェクト)だったり"2021-09-01"のような数値だったりあるいは{ year: 2021, month: 9, day: 1 }のようなオブジェクトだったりします。これらの扱いが面倒なので関数にまとめています。 function getPlainDateWithISOCalendar(date: DateLike) { if (isTemporalDateLike(date)) { const fields = date.getISOFields(); return new Temporal.PlainDate( fields.isoYear, fields.isoMonth, fields.isoDay ); } else if (typeof date === "string") { return Temporal.PlainDate.from(date); } else { return Temporal.PlainDate.from({ ...date, calendar: "iso8601" }); } } この関数では最初のif文の中が注目ポイントです。与えられたオブジェクトがTemporal.PlainDateのようなTemporalオブジェクトだった場合はgetISOFieldsが使用できます。これを用いてISO 8601カレンダーでの日付を得ることができます。直接date.monthやdate.dayなどとすることはできません。なぜなら、dateにはすでに別のカレンダーが紐づいていてISO 8601日付が得られないかもしれないからです。 また、このような場面で迂闊にdateの日付計算を行うと、すでにdateに永遠の夏休みカレンダーが紐づいているかもしれない関係で簡単に無限ループが発生してしまうという罠があります。それを避けるために与えられたdateへの刺激を最小限にし、getISOFieldsの呼び出しだけにしています。getISOFieldsは内部表現を読み出すだけなので、dateへの刺激が小さいと思われます。 実際の計算を行うeternalDateはこのような実装になっています。ISO 8601で8月以降の日付は全部8月に押し込めるような実装です。2021年12月31日までを全部8月に押し込め、それを超えると2022年1月1日になります。よって、永遠の夏休みカレンダーは1月〜8月しかないカレンダーとなります。 与えられた日付が永遠の夏休み日付での8月何日に当たるかを調べるには与えられた日付がISO 8601日付で8月1日の何日後か調べる必要がありますが、Temporalではそれを行なってくれるsinceメソッドが用意されているので簡単です。注意点として、このsinceは永遠の夏休みカレンダーではなくISO 8601カレンダー上で実行する必要があります。 function eternalDate( isoDate: Temporal.PlainDate ): [month: number, day: number, isoDate: Temporal.PlainDate] { if (isoDate.month < 8) { return [isoDate.month, isoDate.day, isoDate]; } const august1st = isoDate.with({ month: 8, day: 1 }); const daysFromAugust1st = isoDate.since(august1st, { largestUnit: "day" }).days + 1; return [8, daysFromAugust1st, isoDate]; } 永遠の夏休み日付からISO 8601日付への変換 逆に、永遠の夏休み日付で「8月50日」などと言われたとき、それがISO 8601日付で何月何日にあたるのか計算できる必要があります。Temporalではあくまで日付の内部表現はISO 8601なので、この計算はとても必要です。 これを実装するにはCelendarのdateFromFieldsをオーバーライドします。fieldsとは、{ year: 2021, month: 8, day: 50 }のような形で日付を指し示すオブジェクトです。 override dateFromFields( fields: Parameters<typeof isoDateFromFields>[0], options: Temporal.AssignmentOptions ): Temporal.PlainDate { const { year, month, day } = isoDateFromFields(fields, options); return new Temporal.PlainDate( year, month, day, eternalSummerVacationCalendar ); } 計算の本体はisoDateFromFieldsですね。内容は先ほどのちょうど逆の計算になっていて、8月の場合は一旦ISO 8601日付の8月1日から出発して何日後という形で計算します。この辺りもTemporalの機能を用いていますので、Temporalを用いる日付計算の参考になるかもしれません。 /** * Converts an eternal date to ISO date. */ function isoDateFromFields( fields: { year: number | undefined; month: number | undefined; day: number | undefined; }, options: Temporal.AssignmentOptions ): { year: number; month: number; day: number; } { let { year = 0, month = 1, day } = fields; if (month === 8) { day = fields.day ?? 1; if ((day < 1 || AugustDays < day) && options.overflow === "reject") { throw new RangeError("Invalid Date"); } day = constrain(day, 1, AugustDays); const august1st = new Temporal.PlainDate(year, 8, 1); const targetDate = august1st.add({ days: day - 1 }); return { year: targetDate.year, month: targetDate.month, day: targetDate.day, }; } if (month > 8) { throw new RangeError("Invalid Date"); } return { year, month, day: day ?? 1, }; } 以上が永遠の夏休みカレンダーの実装の8割くらいを占めています。本当に、ISO 8601による内部表現との変換が主要なタスクであることが分かります。 他にやっていることとして、日付の足し算の処理や月の中に何日あるかというクエリへの対応があります。 永遠の夏休み日付の利用 永遠の夏休み日付からTemporalオブジェクトを作るには次のようにします。 const august32 = Temporal.PlainDate.from({ year: 2021, month: 8, day: 32, calendar: eternalSummerVacationCalendar }); console.log(august32.month, august32.day); // 8 32 console.log(august32.toJSON()); // "2021-09-01[u-ca=eternal-summer-vacation]" 注意しなければいけない点として、このように作ったTemporal.PlainDateオブジェクトはmonthやdayといったプロパティで永遠の夏休み日付を取得することができますが、toJSONなどで文字列化しても2021-08-32のような表記は得られません。 これは、繰り返しになりますが、日付の内部表現はあくまでISO 8601であり、シリアライゼーションなど相互運用性が必要な場面ではISO 8601表現が使われるからです。Calendarは「日付データが時間のどの時点を指すのか」といったことに関与するものではなく、あくまで表示のためのものであると理解しましょう。 まとめ この記事では、8月32日という題材を通してTemporalのCalendarの実装と実用例について学びました。Calendarの実装においては、ISO 8601で表される内部表現と、独自カレンダー上の日付の相互変換が主なタスクであることが分かりました。 これで、通常と異なる時空間に迷い込んでしまった場合でも問題なく日付を扱うことができますね。 サンプル(再掲): GitHubリポジトリ(宣伝): Q & A Q. この実装だと永遠ではないのでは? A. 本当に永遠の夏休みは読者の練習問題とします。年がISO 8601と一致しなくなったりするのでさらに難易度が高くおすすめです。 今回の実装は結構サボっていて、例えば1週間が7日ではないカレンダーだと壊れてしまいます。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

私も JavaScript で 両端キューを書いてみた

これは何? を拝見して。 そういえば、 C++ の deque のようなものって書いたことないなと思って書いてみた。 実装方針 deque のソースコードを見たわけじゃないけど、こんな感じじゃないかなと思っている。 オブジェクトはスロットを何面か持っている。スロットは確保した時点でサイズ固定の配列。 一連のスロットは、概ね単なる配列として持つが、先頭より前とか末尾よりあとに使われないスロットを死蔵することができるようにする。 バッファの先頭は、先頭のスロットに、バッファの末尾は末尾のスロットにある。 末尾への追記を続けてスロットが満タンになったらスロットを末尾に追加(死蔵スロットがあればそれを利用)。 先頭への追記を続けてスロットが満タンになったらスロットを先頭に追加(死蔵スロットがあればそれを利用)。 末尾からの撤去を続けて末尾のスロットがからっぽになったら、末尾のスロットを撤去。撤去されたスロットは開放されず、死蔵される。 先頭からの撤去を続けて先頭のスロットがからっぽになったら、先頭のスロットを撤去。撤去されたスロットは開放されず、死蔵される。 スロット撤去時に死蔵スロットがたくさんあったら開放するが、死蔵スロットをゼロ個にはしない。 新たに作るスロットに格納できる要素の数は、下限を設けつつ、deque の要素数ぐらいのサイズにする。 スロットは概ね単なる配列なので、先頭へのスロット追加時に、スロット数を $S$ とすると $ \mathcal{O} (S)$ の計算が必要になるけど、$S$ 自体が小さいし、発生頻度も低いので平均的には $ \mathcal{O} (1)$ で計算できていると思う。 コードは、スロット部とスロットを利用した deque 部がそれぞれ 50行弱、合わせて 100行弱になった。もうちょっと短く書けると思ったんだけどなぁ。 実装は以下。 100行弱の JavaScript による実装 JavaScript_for_node 'use strict'; const newDeque = () => { const newSlots = () => { const slots = { m: [], beginIx: 0, endIx: 0, }; slots.front = () => { return slots.m[slots.beginIx]; }; slots.back = () => { return slots.m[slots.endIx - 1]; }; slots.size = () => { return slots.endIx - slots.beginIx; }; slots.newBuffer = (s) => { return Array(Math.max(1<<10, s)); }; slots.push_back = (s) => { if (slots.endIx === slots.m.length) { slots.m.push(slots.newBuffer(s)); // console.log(slots.m.map( e=>e.length)); } slots.endIx++; }; slots.push_front = (s) => { if (slots.beginIx === 0) { slots.m.unshift(slots.newBuffer(s)); // console.log(slots.m.map( e=>e.length)); slots.endIx++; } else { --slots.beginIx; } }; slots.pop_back = () => { --slots.endIx; if (slots.endIx + 4 < slots.m.length) { slots.m = slots.m.slice(0, slots.endIx + 2); } }; slots.pop_front = () => { ++slots.beginIx; if (4 < slots.beginIx) { const s = slots.size(); slots.m = slots.m.slice(slots.beginIx - 2); slots.beginIx = 2; slots.endIx = 2 + s; } }; return slots; }; const o = { slots: newSlots(), size: 0, headS: undefined, tailS: undefined, head: undefined, tail: undefined, itemCount: 0, }; o.push = (x) => { if (o.slots.size() === 0 || o.slots.back().length === o.tail) { o.slots.push_back(o.size()); o.tail = 0; if (o.head === undefined) { o.head = 0; } } o.slots.back()[o.tail++] = x; ++o.itemCount; }; o.pop = () => { if (o.size === 0) { return undefined; } if (o.tail === 0) { o.slots.pop_back(); o.tail = o.slots.back().length; } --o.itemCount; return o.slots.back()[--o.tail]; }; o.shift = () => { if (o.size === 0) { return undefined; } if (o.head === o.slots.front().length) { o.slots.pop_front(); o.head = 0; } --o.itemCount; return o.slots.front()[o.head++]; }; o.unshift = (x) => { if (o.slots.size() === 0 || o.head === 0) { o.slots.push_front(o.size()); o.head = o.slots.front().length; if (o.tail === undefined) { o.tail = o.head; } } o.slots.front()[--o.head] = x; ++o.itemCount; }; o.size = () => { return o.itemCount; }; return o; }; module.exports = newDeque; 処理時間 こんな方法で実行時間を測ってみた。 JavaScript_for_node 'use strict'; const newQueue = require('./'+process.argv[2]); const loopCount = 2500; const compareElements = (q) => { while (q.size() != 0) { const b = (q.size() % 2 == 0); b ? q.pop() : q.shift(); } }; const test = () => { console.log("test"); const q = newQueue(); let ix = 0; for (let c = 0; c < loopCount; ++c) { q.push(++ix); for (let i = 0; i < loopCount; ++i) { q.unshift(++ix); q.pop(); } } compareElements(q); }; test(); この計算は前述の記事にとって特に不利なものらしく、実行時間は下表のようになった。 実装 時間 上記の拙作 0.182s ほぼ Array そのまま 1.143s 前述の記事 の実装 9.849s 改善とか調整とかすべきポイント スロットの管理を賢くすると速くなるかもしれないけど、大差ないんじゃないかと思う。 あたらしくスロットを作るときのサイズをどうするのが良いのかわからなかった。根拠なく「1024個 と deque に今入っている要素の数の多い方」ということにした。 ほとんどの環境でこれで困ることはないんじゃないかと想像している。 まあこれもユースケースによって何がベストなのか全然変わってくるとは思う。 スロットを積極的に破棄するとメモリ使用量が減り、スロットの破棄を控えると速度が上がる。これもバランスの問題なので用途によってベストの値が違うと思う。 JavaScript に慣れていないので普通こういう書き方しないよ、という意見がありそうだと思っている。 「先頭から N 番目」というアクセスが定数時間でできると思うんだけど、どうやるんだろ。スロットが複数ある場合に先頭からなめる以外にいい方法があるんだろうか。まあ先頭からなめてもスロット数はそんなに多くならないから困らないわけだけど。 そもそも そもそも。 格納する要素数の最大値が既知ならこんな複雑なものを使う必要はなく、リングバッファを使うのが正解。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

それぞれのメソッド[filter( ),find( ),findIndex( )]

filter( ) filter( )は配列内にある要素が指定された支持にあっていたらそのあっているものだけを配列から取り出して新しい配列の中にいれてくれます。 const characters = ['ab', 'cde', 'fghi', 'jklmn']; const result = characters.filter(character => character.length > 3); console.log(result); => ["fghi", "jklmn"] 上記例だと3文字以上の文字を残し、新しい配列を作ってくれます。 const foods = [ { name: "pork", type: "meet" }, { name: "salmon", type: "fish" }, { name: "beef", type: "meet" }, { name: "tuna", type: "fish" }, ]; const cookingType = materials => { if (materials.type === "meet") { return true; } return false; }; const foodsType = foods.filter(cookingType); console.log("result\n", foodsType); .typeがmeetの要素を取り出し、新しい配列を作ってくれます。 find( ) find( )は同じくその支持にあった最初の値を返してくれます。 const array = [2, 10, 55, 180, 23]; const result = array.find(element => element > 8); console.log(result); => 10 8より大きい数字の最初の10が返ってきました。 findIndex( ) findIndex( )も同じくその支持にあった最初の要素の位置を返してくれて合わなければ−1を返します。 const array = [2, 10, 55, 180, 23]; const number = (element) => element > 20; console.log(array.findIndex(number)); => 2 配列は0から始まるので20より大きい55の位置の2 が返ってきました。 const array = [2, 10, 55, 180, 23]; const number = (element) => element > 181; console.log(array.findIndex(number)); => -1 仮に181を比較対象に入れると-1が返ってきました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[filter( ),find( ),findIndex( )]メソッド JavaScript

filter( ) filter( )は配列内にある要素が指定された支持にあっていたらそのあっているものだけを配列から取り出して新しい配列の中にいれてくれます。 const characters = ['ab', 'cde', 'fghi', 'jklmn']; const result = characters.filter(character => character.length > 3); console.log(result); => ["fghi", "jklmn"] 上記例だと3文字以上の文字を残し、新しい配列を作ってくれます。 const foods = [ { name: "pork", type: "meet" }, { name: "salmon", type: "fish" }, { name: "beef", type: "meet" }, { name: "tuna", type: "fish" }, ]; const cookingType = materials => { if (materials.type === "meet") { return true; } return false; }; const foodsType = foods.filter(cookingType); console.log("result\n", foodsType); .typeがmeetの要素を取り出し、新しい配列を作ってくれます。 find( ) find( )は同じくその支持にあった最初の値を返してくれます。 const array = [2, 10, 55, 180, 23]; const result = array.find(element => element > 8); console.log(result); => 10 8より大きい数字の最初の10が返ってきました。 findIndex( ) findIndex( )も同じくその支持にあった最初の要素の位置を返してくれて合わなければ−1を返します。 const array = [2, 10, 55, 180, 23]; const number = (element) => element > 20; console.log(array.findIndex(number)); => 2 配列は0から始まるので20より大きい55の位置の2 が返ってきました。 const array = [2, 10, 55, 180, 23]; const number = (element) => element > 181; console.log(array.findIndex(number)); => -1 仮に181を比較対象に入れると-1が返ってきました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Browserify を試す(node.jsのrequireメソッドをブラウザで使う)

経緯 node-webrtc のサンプルコード解析して、サーバーサイドのnode.jsのJavaScriptなのか、ブラウザ用のJavaScriptなのか、混乱した node-webrtcについては別途めとめる予定 node-webrtcのサンプルでは、Browsersを用いて「node.js用のJavaScript」を「ブラウザ用のJavaScript」に変換していることがわかった node-webrtcコードを解析できるように、Browsers の最低限の知識を身に着けるためにまとめる 環境 Windows 10 node v16.8.0 npm 7.21.1 npm node-webrtcで使っているnode-fetchのバージョンに合わせている 詳細は下部にあるソースコードを参照ください +-- browserify-middleware@8.1.1 +-- browserify@17.0.0 +-- express@4.17.1 +-- node-fetch@2.6.1 `-- serve@12.0.0 Browserify とは requireメソッドをブラウザで使えるようにする魔法のようなテクノロジー Browsers don't have the require method defined, but Node.js does. With Browserify you can write code that uses require in the same way that you would use it in Node. node-fetch を node.jsで動作させる 実装コード 以下のHTTPリクエストを行うコードを node.js で実行 別途、http://localhost:5000/file.txt を用意しておく HTTPリクエストが成功して、ファイルが取得できることを確認 example.js const fetch = require('node-fetch'); async function Fetch() { const response = await fetch('http://localhost:5000/file.txt', {method: 'GET'}); const body = await response.text(); console.log(body); } Fetch(); 実行結果 HTTPのリクエストに成功 c:\BrowserifyExample>node example.js The test succeeded. require('node-fetch') をブラウザで動作させる パッケージ側にインストール Browserify を実行して、ブラウザで動作するJavaScriptに変換する node ./node_modules/browserify/bin/cmd.js example.js -o public/bundle.js 出力したファイルをHTMLで参照する <!DOCTYPE html> <html> <body> <h1>Open the Console on your browser.</h1> <script src="bundle.js"></script> </body> </html> node.sjのHTTPサーバーを起動してブラウザでFetchできていることを確認 node ./node_modules/serve/bin/serve.js ./public expressフレームワークと連携 browserify-middleware を使うと、サーバーサイドで、「require()のあるnode.js用のJavaScript」を、「ブラウザで処理できるJavaScript」に変換して、返却することができる 以下のようなコードで、express フレームワークと連携できる const browserify = require('browserify-middleware'); const express = require('express'); const app = express(); app.use(`/`, browserify("./example.js")); const server = app.listen(3000, () => { const address = server.address(); console.log(`http://localhost:${address.port}\n`); }); 変換されたJavaScriptのコード ソースコード
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【javascript】テンプレートリテラルとアロー関数を用いた、シンプルな引数有りメッセージ呼出ファンクションの実装検討

背景 Nuxt.js(node.js)にて開発する時、まずメッセージの体系化を行いたい。引数にも対応可能なのが望ましい。 引数含むメッセージファンクション 呼び出し側 上記2要素で完結しつつ、1メッセージに付き1行~2行ぐらいでシンプルにならないかと考えた。 成果 一応できた。 メッセージ側 // アロー関数を用いて記述、実行。 var messages = { example1: (...args:string[]) => escape(`const${args[0]}${args[1]}${args[2]}`) , example2: (...args:string[]) => escape(`テ${args[0]}ス${args[1]}ト`) } // XSS対策のエスケープ function escape(str:string){ str = str.replace(/&/g, '&amp;'); str = str.replace(/</g, '&lt;'); str = str.replace(/>/g, '&gt;'); str = str.replace(/"/g, '&quot;'); str = str.replace(/'/g, '&#39;'); return str } 使っているのはテンプレートリテラルとアロー関数。いずれもIE非対応であることは留意。 https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Template_literals https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions 呼び出し側の自動テストメソッド(Jest) import { messages} from '@/logic/common' // 下記はいずれも成功。 // messages.example2などで入力補完として出てくるためソースを追ったり探したりするのが容易い describe('callMessage', () => { test('messages', () => { expect(messages.example1( '0','3','2')).toEqual('const032') }) test('escape messages', () => { expect(messages.example2( '&','2','1')).toEqual('テ&amp;ス2ト') }) }) メッセージを編集しやすく、複数引数にも対応でき、コーディング視点からも楽。やりたかったことはできていそうと考える。 テンプレートリテラルなので、改行も容易い。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RGBの補色を返す関数を書いた

書き捨てのコードなので、適当に参考にしてください。 実装はTypeScriptですが、同じノリで他の言語もできると思ってます。 wikipedia 補色同士の色の組み合わせは、互いの色を引き立て合う相乗効果があり、これは「補色調和」といわれる。 しかし、純色など、明度が同じ補色同士を組合せた場合は、ハレーションを引き起こして、目がチカチカしてしまう // FFFFFF, 00FF1E などの文字列を受け取り、その補色を設定して返却する getComplementaryColor(rgbStr: string): string { const R = rgbStr.substr(0, 2).toLowerCase() const G = rgbStr.substr(2, 2).toLowerCase() const B = rgbStr.substr(4, 2).toLowerCase() const r = ('00' + (255 - parseInt(R, 16)).toString(16)).slice(-2) const g = ('00' + (255 - parseInt(G, 16)).toString(16)).slice(-2) const b = ('00' + (255 - parseInt(B, 16)).toString(16)).slice(-2) return r + g + b }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TypeScriptの関数で最低限押さえておきたいポイント

この記事について TypeScriptの関数で最低限知っておきたいポイントを駆け足でまとめてみました。各種深掘りはしないので予めご了承ください。 レストパラメーター 型安全な可変長引数を引数として受け取る場合に使用します。 Rest parameters are treated as a boundless number of optional parameters. When passing arguments for a rest parameter, you can use as many as you want; you can even pass none. The compiler will build an array of the arguments passed in with the name given after the ellipsis (...), allowing you to use it in your function. 参考文献:「Rest Parameters」 function getTotal(...numbers: number[]): number { let total = 0; numbers.forEach((num) => total += num); return total; } console.log(getTotal()); // 0 console.log(getTotal(10, 20)); // 30 console.log(getTotal(10, 20, 30)); // 60 注意点としては1つの関数につき最大1つまでしかレストパラメーターは使えません。(呼び出し元の構文から推測できると思いますが) thisの型付け JavaScriptにおいてcallメソッドを使ってメソッドに引数を渡す場合、thisがメソッド側が期待するオブジェクトでなかった時に実行時エラーを起こしてしてしまうというリスクが存在します。 function fancyDate(){ return `${this.getMonth() + 1}/${this.getDate()}/${this.getFullYear()}` } console.log(fancyDate.call(new Date)) // "8/30/2021" TypeScriptではcallメソッドなどで渡すthisの型を指定することが可能で、要求するthisをbindしなかった場合は型エラーを起こしてくれます。 function fancyDate(this:Date){ return `${this.getMonth() + 1}/${this.getDate()}}/${this.getFullYear()}` } fancyDate.call(new Date) // lib.es5.d.ts(342, 67): An argument for 'thisArg' was not provided. fancyDate.call() ジェネレーター function *という構文を使うとジェネレーター関数を生成することができます。 function * is the syntax used to create a generator function. Calling a generator function returns a generator object. The generator object just follows the iterator interface (i.e. the next, return and throw functions). 参考文献:「Generators」 ジェネレーター内部でyeildで戻り値を指定することが可能で次の呼び出しまで実行は休止されるので、一見無限ループしそうなコードも呼び出し毎にループさせることができます。(nextメソッドを呼ぶことで1ループさせることができます) function* infiniteSequence() { var i = 0; while(true) { yield i++; } } var iterator = infiniteSequence(); while (true) { console.log(iterator.next()); // { value: xxxx, done: false } forever and ever } イテレーター イテレーターは一連の値を利用するための方法、反復可能なオブジェクトです。またにオブジェクト指向言語におけるデザインパターンの一種でもあります。 Iterator itself is not a TypeScript or ES6 feature, Iterator is a Behavioral Design Pattern common for Object oriented programming languages. 参考文献:「Iterators」 ここでいう反復可能なオブジェクトとは以下のinterfaceを実装するオブジェクトです。 interface Iterator<T> { next(value?: any): IteratorResult<T>; return?(value?: any): IteratorResult<T>; throw?(e?: any): IteratorResult<T>; } 以下のコードは「TypeScript Deep Dive」に記載されていたフィボナッチ計算のプログラムです。 class Fib implements IterableIterator<number> { protected fn1 = 0; protected fn2 = 1; constructor(protected maxValue?: number) {} public next(): IteratorResult<number> { var current = this.fn1; this.fn1 = this.fn2; this.fn2 = current + this.fn1; if (this.maxValue != null && current >= this.maxValue) { return { done: true, value: null } } return { done: false, value: current } } [Symbol.iterator](): IterableIterator<number> { return this; } } let fib = new Fib(); fib.next() //{ done: false, value: 0 } fib.next() //{ done: false, value: 1 } fib.next() //{ done: false, value: 1 } fib.next() //{ done: false, value: 2 } fib.next() //{ done: false, value: 3 } fib.next() //{ done: false, value: 5 } let fibMax50 = new Fib(50); console.log(Array.from(fibMax50)); // [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ] let fibMax21 = new Fib(21); for(let num of fibMax21) { console.log(num); //Prints fibonacci sequence 0 to 21 } IterableIteratorのinterfaceは次のように定義されていますのでnext(value?: any): IteratorResult<T>と[Symbol.iterator](): IterableIterator<T>を実装しなくてはいけません。 interface IterableIterator<T> extends Iterator<T> { [Symbol.iterator](): IterableIterator<T>; } 呼び出しシグネチャ 呼び出しシグネチャを使用すると呼び出し可能なプロパティを定義することができます。 In JavaScript, functions can have properties in addition to being callable. However, the function type expression syntax doesn’t allow for declaring properties. If we want to describe something callable with properties, we can write a call signature in an object type: 参考文献:「Call Signatures」 type DescribableFunction = { description: string; (someArg: number): boolean; }; function doSomething(fn: DescribableFunction) { console.log(fn.description + " returned " + fn(6)); } オーバーロードされた関数の型 オーバーロードを行うと関数に対して、複数の型を定義することができます。次のコードは「TypeScript Deep Dive」に掲載されていたサンプルコードで、オーバーロードを使うと次のようにpickCardに対して2パターンの引数・戻り値の型を定義することができます。 let suits = ["hearts", "spades", "clubs", "diamonds"]; function pickCard(x: { suit: string; card: number }[]): number; function pickCard(x: number): { suit: string; card: number }; function pickCard(x: any): any { if (typeof x == "object") { let pickedCard = Math.floor(Math.random() * x.length); return pickedCard; } else if (typeof x == "number") { let pickedSuit = Math.floor(x / 13); return { suit: suits[pickedSuit], card: x % 13 }; } } let myDeck = [ { suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }, ]; let pickedCard1 = myDeck[pickCard(myDeck)]; alert("card: " + pickedCard1.card + " of " + pickedCard1.suit); let pickedCard2 = pickCard(15); alert("card: " + pickedCard2.card + " of " + pickedCard2.suit); 注意しなければいけない部分としてfunction pickCard(x): anyはオーバーロードに該当しないので上のコードでは2つのオーバーロードが存在するといった形になります。 オーバーロードリストに含まれないstringをpickCardの引数として渡すとコンパイルエラーになります。 // No overload matches this call. // Overload 1 of 2, '(x: { suit: string; card: number; }[]): number', gave the following error. // Argument of type 'string' is not assignable to parameter of type '{ suit: string; card: number; }[]'. // Overload 2 of 2, '(x: number): { suit: string; card: number; }', gave the following error. // Argument of type 'string' is not assignable to parameter of type 'number'.(2769) let pickedCard2 = pickCard("i"); alert("card: " + pickedCard2.card + " of " + pickedCard2.suit); 参考文献: O'Reilly Japan - プログラミングTypeScript TypeScript Rest Parameters Generators Rest Parameters Call Signatures Iterator
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[jQuery]ランキングしたものをslickで表現する

はじめに 本記事では、slickを用いた記述をいたします。 slickはjQueryベースのスライダーを作成するためのプラグインです。 コード いろんなサイトやYouTubeの動画を参考にしましたが、 CDNを使用するのが一番良いなという結論に至っています。 (ファイル保存とか色々グダってトラウマ) <!DOCTYPE html> <html> <head> 省略 <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick-theme.css"/> <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.css"/> </head> <body> <%= yield %> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script type="text/javascript" src="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.min.js"></script> </body> </html> これだけです。 あと、gem 'jquery-rails'は予めbundle installしています。 以下長いのでランキングの部分だけ。 <h2 class="ranks-title">Ranking !</h2> <div class="ranks"> <% @ranks.each do |food| %> <div class="ranks-img-content"> <h2 class="food-post-user"> <%= link_to user_path(food.user_id) do %> <%= food.user.nickname %> <% end %> </h2> <%= link_to food_path(food) do %> <%= image_tag food.image, class: "food-img" if food.image.attached? %> <div class="food-img-info"> <h2 class="shop-name"> <%= food.shop_name%> </h2> <h2 class="food-name"> <%= food.food_name%> </h2> </div> <% end %> </div> <% end %> </div> <script type="text/javascript"> $(function() { $('.ranks').slick({ dots: true }); }); </script> ドットは付けたいなと思ったので、即記述しました。 矢印が画面外にいってしまっているので、明日調整しようと思います。 また、 scriptタグを一番下にして記述しましたが、まあまあ汚いコードだと思っています。 ここ含めてリファクタリングのこと考えないとな。 ランキングのことをお話ししているので、 コントローラーも記載します。 def index @foods = Food.order("created_at DESC") @like = Like.new @ranks = Food.find(Like.group(:food_id).order('count(food_id) desc').limit(5).pluck(:food_id)) end いいね数が多い順番で、上から5位までをとっています。 難点なのが、過去の投稿全てのランキングになってしまっているので、改善が必要。 1週間や1ヶ月にいいね獲得数ランキングにするのが一番ベスト。 考えます。 また、リファクタリングの点では、モデルに記載するのが良いですが、 出来上がったことの満足感で後回しにしてしまっています。 後々改善します。 終わりに 導入しましたが、プラグインは便利でかっこいいですね。 導入後、楽しくていっぱい遊んでしまいます。 矢印が画面外にいる点が気持ち悪いので、明日の目標です。 以下参考サイトです。 【Rails】 便利なpluckメソッドをマスターしよう! Railsで週間いいねランキングを作る! slickの公式サイト 【Rails】slickでスライドショーを表示させる slickの使い方からカスタマイズまで【スライダープラグイン決定版】 jQueryスライダープラグイン9選!スライドショーを手軽に作成 どこよりも詳しい万能スライダーjQueryプラグインslick.jsの使い方 【jQuery】一行でスライダーが動く!slickをご紹介! それでは、明日も頑張りましょう!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LinkedIn Jobsに人材紹介会社からの求人広告をマーキングする

経緯 最近"LinkedIn Jobs"に仕事を探し、そこにはたくさんの人材紹介会社からの求人広告が載せている。たくさんの時間を使ってこれを飛ばすのは困るので、だからこのコードを作った。 このコードはJavaScriptで書かれて、外部ライブラリは使わない。Google Chromeに試したが、他のウェブブラウザが使われると思う。 どうやって使うの 変数"agentList"に人材紹介会社のリストを入れる let agentList = [ "ADD YOUR LIST INTO HERE" ]; function highlightAgent() { document.querySelectorAll("a[data-control-name='job_card_company_link']").forEach(function(currentValue) { let name = currentValue.text.trim(); let agentName = agentList.find(function(input) {return input === name}); if (agentName !== undefined) { currentValue.style.backgroundColor = "#FF0000"; } }); } window.setInterval(highlightAgent, 5000); "デベロッパーツール"の"Console"に上のコードを貼り付けてください(Google Chromeが使う場合)。マーキングのコードは毎5秒実行するので、次のページに押しても、5秒にはマーキングが見られる。 次のページに押しても、コードは有効する。無効する時にはウェブブラウザの"再読み込み"を押してください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む