【JavaScript】二つの配列をチェックし重複を消して配列として出力する
こんばんは葛の葉です。今回もミニサイズで。
二つの配列をチェックし重複を消して配列として出力する
スプレッド構文とSetを使って重複削除をします。比較的新しいEcmaScriptになるので、古いやり方だとどうやるかはちょっとわからない。。。
const l1 = [1, 2, 3, 4, 5]; const l2 = [2, 3, 4, 5, 6]; const l3 = [...new Set([...l1, ...l2])]; console.log(l3); // [ 1, 2, 3, 4, 5, 6 ] console.log(l1); // [ 1, 2, 3, 4, 5 ] console.log(l2); // [ 2, 3, 4, 5, 6 ]
くるしいなぁこれ
Setはスプレッド構文で配列へ
Set型は...
スプレッド構文によって分解することが出来ます。その分解された各値を[]
配列で囲ってあげると配列になることができます。
const hoge = [1,2,3,4,5,5]; const piyo = new Set(hoge); console.log(piyo); // Set { 1, 2, 3, 4, 5 } console.log([...piyo]); // [ 1, 2, 3, 4, 5 ] console.log(Array.isArray([...piyo])); // true
【TypeScript】MongoDBのBulkWriteと戦う
こんばんは葛の葉です。今回はMongoDBのBulkWriteについて書いていきます。
公式ドキュメントはこちら
MongoDB本体のドキュメント
NodeのMongoDBドライバーのドキュメント
version
packageのversionはこんな感じです。
{ "dependencies": { "mongodb": "^3.5.5" }, "devDependencies": { "typescript": "^3.8.3", "@types/mongodb": "^3.5.5" } }
docker-compose.ymlはこちら
version: "3" services: mongo: image: mongo:4.2.2-bionic ports: - 27017:27017
今回の記事に使用したGithubリポジトリはこちら
Bulk Writeって日本語でそもそもどんな意味なのだろう
Googleの翻訳結果では以下のようになりました。
Bulk Write
=> 一括書き込み
一括書き込み…そのまんまの意味だなぁ。
すなわち、同じコレクション内で複数の処理をMongoDBに依頼出来るということになるみたいです。
さっそく使ってみる
今回は事前にこのようなデータを入れてみる。
[ { name: "MoterCycle", colors: ["red", "blue", "green", "three"], like: true, }, { name: "Car", colors: ["blue", "red", "silver"], like: false, }, { name: "Bike", colors: ["yellow", "white", "silver"], like: false, }, ];
これらのドキュメントを以下のように変更しようと思います。
- nameがKickBoardのドキュメントを追加
- nameがCarのlikeプロパティをtrueに更新
- colorsプロパティにblueを持つドキュメントについてaonanokaプロパティ(true)を追加
これらをBulkWriteで書き込むことが出来ます。上記の変更を行うようなそれぞれのクエリを書き、それらクエリを配列に入れてbulkwrite
メソッドに渡せばよいのです。
const queries = [ // nameがKickBoardのドキュメントを追加 { insertOne: { document: { name: "KickBoard", colors: ["silver", "black"], like: false, }, }, }, // nameがCarのlikeプロパティをtrueに更新 { updateOne: { filter: { name: "Car" }, update: { $set: { like: true } }, }, }, // colorsプロパティにblueを持つドキュメントについてaonanokaプロパティ(true)を追加 { updateMany: { filter: { colors: "blue" }, update: { $set: { aonanoka: true } }, }, }, ]; await collection.bulkWrite(queries);;
最終的にはこのコレクションはこのようになります。_id
はMongoDBが勝手に発番する一意な番号です。
[ { _id: 5e9095000d4f3b2309188f89, name: 'MoterCycle', colors: [ 'red', 'blue', 'green', 'three' ], like: true, aonanoka: true }, { _id: 5e9095000d4f3b2309188f8a, name: 'Car', colors: [ 'blue', 'red', 'silver' ], like: true, aonanoka: true }, { _id: 5e9095000d4f3b2309188f8b, name: 'Bike', colors: [ 'yellow', 'white', 'silver' ], like: false }, { _id: 5e9095000d4f3b2309188f8c, name: 'KickBoard', colors: [ 'silver', 'black' ], like: false } ]
思ったとおり、やりたいことが出来たかとおもいます。
【検索】検索で公式ドキュメントを掘り起こしたいとき
こんばんは葛の葉です。
まぁ私もこんな取るに足らないブログ書いてる私がこんなこと言うのもなんなんですけど、Google検索で調べものをしているときにブログや某IT系記事がちょっと邪魔くさいなって思うときがあるんですよね。モジュールやミドルウェアに何かやってもらおうとするなら、やっぱり公式のドキュメントがとても大事です。
私のブログなんかもそうですけど、結局のところ個人が書いている情報なんていつの情報かわからないですし(その記事の掲載日がそのときの最新の書き方とは限らないため)、内容も途中までのことしか書いてないから実践レベルで必要なことまで書かれていないことがあるんですよね。
やっぱり公式のドキュメントをじっくり読むのがなんだかんだで正解で近道になるような気がするんです。
Nginxのリダイレクト方法について調べる
Webサーバーとしてかなり有名になってきたNginxですが、このNginxにリダイレクトしてもらおうとConfに書く内容を調べようと以下のキーワードで検索をかけました。
nginx redirect
結果は以下の通り
公式のドキュメントが出てくるのは二ページ目!ちょっと前から思ってることなんですけど、これって行き過ぎたSEO対策が原因なんじゃないかなぁって思うんですよね。。。
こういう風にして公式ドキュメントを掘り当てる
nginx document redirect
と言った風にdocument
という文言を追加してあげます。
ミドルウェア等は海外製が多く、またその公式ドキュメントには日本語が全くと言っていいほど存在しません。そのため、検索結果で出てきた日本語のページというのは公式のものではないと判断して無視します。時々日本語のドキュメントがあるものもありますけど、翻訳作業が挟むためか内容が少し前のものが出てくることが多いような気がします。
また、公式のURLっぽいドメインかどうか(.comやorgといったメジャーなトップレベルドメイン、そのミドルウェア名がそのまま使われているようなシンプルな独自ドメイン等がだいたい公式っぽい)も見ておきます。
そうやってふるいにかけると以下のようなページが絞られます。
1つめ
3つめ
ちょっと大変ですが、Google翻訳を使って翻訳します。これでだいたいやりたいことが理解できます。
書いてて思ったこと
こういう検索方法も大事なノウハウだよなぁと思います。
【TypeScript】おぉ!って思ったJSの書き方
こんばんは葛の葉です。
体調を崩したので今回も省エネです。
JavaScriptを書き始めてはや9ヶ月ぐらいなんですけど、最近、これいいなぁって思うJSの構文を書いていきます。
スプレッド構文
Mozillaのリファレンスはこちら。
これは配列の要素をバラして展開できる構文のようです。一回、配列の要素をバラすので一度ばらしてから配列に再び挿入することで新しい配列を作成することが出来ます。unshift()
やpush()
みたいな破壊的ではない点も良いですし、一度...
の意味をわかれば、コード理解もシンプルになりやすいという点もありますね。例えば配列に要素を追加するときに前方に追加しているか、後方に追加しているかというのもわかりやすいですね。
const lists = ["hoge", "piyo", "fuga"]; console.log(...lists); // hoge piyo fuga; const newData = "moge"; const newLists1 = [...lists, newData]; const newLists2 = [newData, ...lists]; console.log(lists); // [ 'hoge', 'piyo', 'fuga' ] console.log(newLists1); // [ 'hoge', 'piyo', 'fuga', 'moge' ] console.log(newLists2); // [ 'moge', 'hoge', 'piyo', 'fuga' ]
分割代入
Mozillaのリファレンスはこちら。
簡単に書くと配列を使って複数の変数を一文で代入出来るものです。先のスプレッド構文を使うことで、配列化することも出来るようですね。
const [x, y, ...somethings] = ["cat", "dog", "pig", "horse", "rabbit"]; console.log(x); // cat console.log(y); // dog console.log(somethings); // [ 'pig', 'horse', 'rabiit' ]
字詰め
Mozillaはこちら。2つあるのは字詰めするのが左か右かの違いになります。pad
はpadding(詰める)の意味だと思います。この構文はEcmaScript2019の構文で比較的新しめのものなので、使えないブラウザもありうる。StringオブジェクトがもつpadXXXX
のメソッドに対して、最大の文字数
と詰める文字
を渡してあげればOKというのもわかりやすいです。地味にゼロパディング使うケースがあるのでこういうのはありがたいです。
console.log("7".padStart(3, "0")); // 007 console.log("7".padEnd(3, "0")); // 700
【TypeScript】Reactを触っている
こんばんは、葛の葉です。 最近、個人でReactを触っています。
Reactはもはや説明不要なほど有名なフロントエンド向けのJSフレームワークです。
JSX(TypeScriptならTSX)を使うことが独特。
ReactはJSXという専用の拡張構文を使用します。
import React from "react"; import * as ReactDom from "react-dom"; const Sidebar = () => { return ( <div> <p>ほげほげ</p> </div> ) }; ReactDom.render(<Sidebar />, document.getElementById("root"));
Sidebar関数のような関数を作り、さらに関数の返り値にHTMLのようなものを記載します。そのSidebar関数をReactDom.rendorから呼び出します。ちょっと奇妙なのが<関数名 />
となっているところです。これがReact独特の記載方法になります。Sidebar関数と便宜的に書いているのですが、実はこれ、Sidebarコンポーネントということになります。サイドバーという画面コンポーネントを作成し、それをReactが描写しているのが上記のコードになります。
UIをきれいに作るために、JSXからトランスパイル時にそのCSS向けに記述し直してくれるCSSフレームワークを使うと簡単できれいなウェブ画面が作成できます。
また、Rectではコンポーネントの持つ状態を管理するReduxという別のパッケージもありましたが、昨今ではReact Hooksという公式で同様な機能を持つようになりました。
Reducerの機能はちょっと複雑ですが頑張って取得していきたいです。
【TypeScript】JSから投げるhttpリクエストのエラーコードHPE_INVALID_CONSTANTを出力
こんばんは葛の葉です。
URLを集めている業務がありまして、httpリクエストを投げてステータスコードを集めているのですが、そのときに時々に発生したエラーコード「HPE_INVALID_CONSTANT」に随分悩まされました。
そのときにこのツイートを見かけてクララが立ちました。
TIL: Node.js in *some* instances throws a HPE_INVALID_CONSTANT if the Content-Length header in a http request has the wrong value.
— Erwin Wessels (@htwj) 2019年5月30日
つまり、HttpヘッダーのContent-Length
の値が実際と違う場合にHPE_INVALID_CONSTANT
が発生するようです。
Version
{ "node": "12.13.1", "request": "2.88.2", "request-promise": "4.2.5" }
今回もお世話になっているhttpモジュールとRequest-Promiseモジュールを使います。
再現してみよう
以下にソースを書きます。
import http from "http"; import * as rp from "request-promise"; // Initilaize const port: number = 9991; const host: string = `http://localhost:${port}/`; // Create Http Server const server = http.createServer((_, response) => { // Content-Lengthが1byteと宣言する。 response.setHeader("Content-Length", 1); // 実際のデータは3byteでContent-Lengthとは矛盾する。 response.write("aaa"); response.end(); }); // Http Server Run server.listen(port, () => { console.log(`listen ${host}`); }); (async () => { try { // Http Get Request to Http Server ここでParse Errorが発生しCatchされる。 const hoge = await rp.get(host); // Print Http Response console.log(hoge); } catch (e) { // Print Error Code console.log(e.error.code); // HPE_INVALID_CONSTANT //console.log(e.error.rawPacket); //console.log(Buffer.from(e.error.rawPacket).toString()); } finally { server.close(); } })();
const hoge = await rp.get(host);
のタイミングでHttpRequestを上記の作成したHttpサーバーに投げています。そのタイミングでParseErrorというエラーが発生します。そのエラーをキャッチした際に表示されるエラーコードがHPE_INVALID_CONSTANT
になります。
ブラウザで見るとちゃんと表示されてたりするので、一見すると問題なさそうなページに対し、プログラムからHttpRequestを投げるとHPE_INVALID_CONSTANT
というエラーコードが出力されて面食らうというのが調べるに至った経緯というわけになります。
元データはrawPacketへ
キャッチしたエラーにrawPacketが入っている場合があります。バイナリデータになっているのでStringに変換してあげると色々と使えるかもしれないです。
} catch (e) { console.log(Buffer.from(e.error.rawPacket).toString()); } finally { server.close(); }
HTTP/1.1 200 OK Content-Length: 1 Date: Fri, 13 Mar 2020 16:47:19 GMT Connection: close aaa
ヘッダーに関してはこのままだと使いにくいので、色々と加工してくれるhttp-headersモジュールなんかを使うと良いと思います。
} catch (e) { const packet = Buffer.from(e.error.rawPacket).toString() console.log(parse(packet))
{ version: { major: 1, minor: 1 }, statusCode: 200, statusMessage: 'OK', headers: { 'content-length': '1', date: 'Fri, 13 Mar 2020 16:53:31 GMT', connection: 'close' } }
Bodyデータは。。。。頑張って加工して使ってみてくれ😥
【TypeScript + AWS】CloudWatchLogsからデータを引っ張りたかったんだ
こんばんは、葛の葉です。
今回はAWSのCloudWatchLogsにたまったログに検索をかけ、それを吐き出すということをやっています。いくつか大変なことがあったので、それを書いていこうと思います。
CloudWatchLogsクラスからインスタンスを作る前に
awsのsdkを使うにはconfigにユーザー情報を渡してあげる必要があります。ユーザーの情報をいくつか渡す方法にcredentialsファイルを用いた認証方法があります。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-files.html
credentialsファイルを用いた認証を行う場合は、awsのGlobalConfigInstanceにSharedIniFileCredentialsクラスを使用する必要があります。
なお、SharedIniFileCredentialsのコンストラクタにはcredentialsファイルに記載したプロファイルを指定することができます。
import * as aws from "aws-sdk"; // CredentialFailsを./aws/から読み込む明示的処理。 aws.config.credentials = new aws.SharedIniFileCredentials({profile: "kuzunoha"}); const clw = new aws.CloudWatchLogs();
credeantialsファイル
[kuzunoha] aws_access_key_id = ****** aws_secret_access_key = ******
configファイル
[kuzunoha] region = ap-northeast-1 output = json
大変なこと
aws cliだと勝手にcredentialsファイル読んでくれた気がするのに、同じようにsdkでも読んでくれたらよかったのになーと。credentialsの情報が誤ってんのかとずっと思ってました。はー。
startQuery関数に渡す時間はEpoch時間から引いた時間・・・?
CloudWatchLogsクラス内のstartQueryメソッドの引数となるインターフェイスはStartQueryRequestというものになります。
StartQueryRequest内のstartTimeとendTimeはそれぞれ検索の開始と終わりの日時の情報を渡すのですが、これがUnixタイムを用います。
LinuxでUnixタイムを用いるにはDateオブジェクトのgetTimeメソッドを用います。
import * as aws from "aws-sdk"; const logGroupName = "あなたの素敵なロググループ"; const queryString = "あなたの素敵なクエリ"; const startTime = new Date("あなたの検索したい時間").getTime(); const endTime = new Date("あなたの検索したい時間").getTime(); // CredentialFailsを./aws/から読み込む明示的処理。 aws.config.credentials = new aws.SharedIniFileCredentials() const clw = new aws.CloudWatchLogs(); (async () => { const req = await clw.startQuery({ startTime, endTime, logGroupName, queryString }).promise() // 処理に続く })();
大変なこと
getTime関数はミリ秒を含めるけど、startTimeでミリ秒込みで良いのかなんとも書かれていないんですよね。。。。
getQueryResults関数は何度も叩こう。
getQueryResults().promise()
はプロミスでWebAPIを叩いているようで、その値はGetQueryResultsResponseという型で受け取れます。
getQueryResultsにはstatusがあり、"Scheduled"|"Running"|"Complete"|"Failed"|"Cancelled"のいずれかがあります。
検索結果をWebAPIで提供しているみたいなので、WebAPIを読みに行くことはすぐ終わるのですが、その検索結果がWebAPIに反映されるのはちょっと時間がかかります。Completeとなれば完了するので、そのステータスになるまで何度か叩いてあげます。
import * as aws from "aws-sdk"; const logGroupName = "あなたの素敵なロググループ"; const queryString = "あなたの素敵なクエリ"; const startTime = new Date("あなたの検索したい時間").getTime(); const endTime = new Date("あなたの検索したい時間").getTime(); // CredentialFailsを./aws/から読み込む明示的処理。 aws.config.credentials = new aws.SharedIniFileCredentials() const clw = new aws.CloudWatchLogs(); const wait = (ms:number) => { return new Promise(resolve => setTimeout(resolve, ms)); }; (async () => { const req = await clw.startQuery({ startTime, endTime, logGroupName, queryString }).promise() let response: aws.CloudWatchLogs.GetQueryResultsResponse; // response.statusがComplete,Failed,Cancelledの何れかになるまで何度か叩いて確認する。 do { await wait(1 * 1000); response = await clw.getQueryResults({ queryId: req.queryId }).promise(); } while (["Scheduled", "Running"].includes(response.status)); console.log(response); })();
たいへんだったこと
何回かたたかなきゃいけないんだ。。。。
ピンポイントな情報はQiitaやStack Overflowに載ってないこともあるんだ
巨人の肩に乗るってのはこういう大変なことがあるんですねぇ。いや、ずいぶん楽で便利させてもらってますけどね。ドキュメント読んで頑張りましょう〜