らばーいもっきんぐ

プログラミング等々についての覚え書き

【シェル】bashで改行区切りの複数行を標準入力する方法

はじめに

bashスクリプトを書く際に、改行区切りの複数行をキーボードから入力し、配列として扱う方法を調べたのでメモ。

やりたい事

2021-01-01 10:00:00  
2021-01-02 10:00:00  
2021-01-03 10:00:00  

のように、スペースを含む日時を複数行入力した際に、改行区切りで繰り返し処理を実行したい。

やり方

#!/bin/bash
IFS=$'\n'
input=$(</dev/stdin)

for v in ${input[@]}
do
  echo $v
done

解説

IFSとは

IFS(Internal Field Separator)は文字の区切りを設定するための環境変数bashではデフォルトで「スペース」「タブ」「改行」が設定されている。
今回の場合は入力文字列にスペースが含まれているため、スペースで区切られてしまうのを防ぐためにIFS=$'\n'を指定することで改行区切りに設定している。

/dev/stdinとは

/dev/stdinは標準入力(通常ではキーボード)を表しており、input=$(</dev/stdin)とする事でキーボードから入力された値を変数inputに代入する。

【Flutter】ReorderableListViewの並び替え時の背景色を変更する方法

はじめに

Flutter の ReorderableListView を使用して項目の並べ替えをする際に、デフォルトでは選択した項目の背景色が白色になっていますが、この背景色を変更する方法について調べました。

並び替え時に背景が白になる挙動
選択した項目の背景色が白のため違和感がある

結論

こちらの Issue にある通り、Theme ウィジェットでラップすることで解決することができます。

以下解説。

ReorderableListView とは

www.youtube.com

api.flutter.dev

ReorderableListViewはリスト表示された項目をドラッグアンドドロップで簡単に並び替えることができるウィジェットです。

並び替え可能なリストを簡単に実装できる便利なウィジェットですが、並び替える項目の背景色はデフオルトでは白色になっています。

そのため背景色が白色でない場合は、並び替えをする時だけ部分的に背景が白色になってしまい、全体のデザインを損なう可能性があります。

並べ替え時の背景色を変更する方法

return Theme(
    data: ThemeData(
        canvasColor: Colors.transparent
    ),
    child: ReorderableListView(...)
)

並べ替え時の背景色を変更するには、上記のように ReorderableListView ウィジェットを Theme ウィジェットでラップします。

さらに、ThemeDataのcanvasColorプロパティを使い、背景色を透過することで本来の背景色が表示されるようにします。

上書きする際に指定する色は Colors.transparent を指定しています。

これは背景を透過する指定のため、デフォルトでは白色になっているものを透過させ、本来の背景色を表示しています。

背景色を変更した後の挙動
背景色を変更することで自然な見た目になりました

Theme ウィジェットとは

flutter.dev

Theme は、テーマカラーやフォントスタイルを指定する事でアプリ全体のデザインを統一するために利用されます。 今回は Theme ウィジェットを使って部分的に上書きをする事で背景色を変更しています。

まとめ

Flutter の Theme はトップレベルで一度設定するだけと思っていましたが、部分的にスタイルを上書きする場合にも使える事が分かりました。 ただ、もっとスマートなやり方は無いのかなという気もします。(ReorderableListView のプロパティで設定できるとありがたい)

参考

https://flutter.dev/docs/cookbook/design/themes https://itome.team/blog/2019/12/flutter-advent-calendar-day12/

【JavaScript】Object.assignでオブジェクトのコピーを作る

はじめに

参照型のデータを扱う際に、Object.assignメソッドを使ってオブジェクトのコピーを作る方法を学んだのでその記録です。

Object.assignとは

MDNの定義は以下。

Object.assign() メソッドは、すべての列挙可能なプロパティの値を、1つ以上のコピー元オブジェクトからコピー先オブジェクトにコピーするために使用されます。戻り値としてコピー先オブジェクトを返します。

使用例はこちら。

let a = { x: 1, y: 2, z: 3 };
let b = { x: 5, y: 6, };
let c = { x: 7 };

//戻り値としてコピー先のオブジェクト(a)を返す
Object.assign(a, b, c); //=> { x: 7, y: 6, z: 3 }

//コピー先のオブジェクト(a)は変更される
//コピー元のオブジェクト(bとc)は変更されない
console.log(a); //=> { x: 7, y: 6, z: 3 };
console.log(b); //=> { x: 5, y: 6, };
console.log(c); //=> { x: 7 };

Object.assignは第一引数の値(a)がコピー先になり、その後に続く値(b,c)がコピー元になる。

コピー先の値とコピー元の値がマージされる。

マージする際に、コピー先とコピー元に同じプロパティが存在する場合は、コピー元の値が優先され、コピー先の値は上書きされる。

コピー元の値が複数ある場合(上記例のbとc)は後のものが優先される(bよりcが優先される)

上記の例では、変数aのyの値は変数bのyで上書きされ6になる。変数aのxの値は変数bのxの値で上書きされ、さらに変数cのxの値で上書きされるため、結果的に7になる。

Object.assingでコピーを作る

例えば以下のような場合。

let a = { x: 1, y: 2, z: 3 };
let b = a;

console.log(b); //=> { x: 1, y: 2, z: 3 };

a["x"] = 10;

console.log(a); //=> { x: 10, y: 2, z: 3 };
console.log(b); //=> { x: 10, y: 2, z: 3 };

JavaScriptでは配列やオブジェクトは参照型のため、aの値が変更されるとbの値も変更されてしまう。

そこで、aのコピーを作り、それを利用すればbに影響を与えずに済む。 コピーを作るには以下のようにする。

let a = { x: 1, y: 2, z: 3 };
let b = a;
// aのコピーを作り、変数cに代入する
let c = Object.assign( {}, a );

// 変数cにはaの値がコピーされている
console.log(c) //=> { x: 1, y: 2, z: 3 };

// 変数cの値を変更する
c["x"] = 10;

console.log(a) //=> { x: 1, y: 2, z: 3 };
console.log(b) //=> { x: 1, y: 2, z: 3 };
console.log(c) //=> { x: 10, y: 2, z: 3 };

Object.assignの第一引数(コピー先)に空のオブジェクトを用意し、コピー元の要素を第二引数以降に指定する。

aとcは別のオブジェクトになるため、cの値をいくら変更してもコピー元のaには影響せず、また、aを参照するbにも当然ながら影響を与えない。

まとめ

参照型の挙動を意識していないと、思わぬところに影響を与える可能性があるので注意しましょう。

【JavaScript】Promiseについて学んでみる

はじめに

JavaScriptのasync/awaitについて調べたら、その前にPromiseを理解する必要がありそうだったので、まずPromiseについて調べてみた。

Promiseとは

MDNでPromiseについて調べると、以下のように書かれている。

Promise は非同期処理の最終的な完了もしくは失敗を表すオブジェクトです。

雑にまとめると以下のような感じ。

  • PromiseはES2015で導入された機能で、非同期処理と一緒に使われることが多い。

  • Promiseを使うと非同期処理の実行結果が成功した場合の処理と、失敗した場合の処理を簡潔に記述することができる。

Promiseオブジェクトを定義する

まずはPromiseの定義方法から。 Promiseは以下のように定義することができる。

new Promise(function(resolve, reject){ 
  //非同期の処理 
})

Promiseオブジェクトをインスタンス化する際には引数に関数を指定する(上記のfunctionの部分)。

この関数には最初から2つのパラメーターresolverejectが渡されている。 resolveとrejectは、どちらもそれ自体が関数で、その後に記述する非同期処理の成功と失敗を通知するために用いられる。

非同期の処理が成功した場合は、resolve関数に成功時に通知するメッセージや値などを渡す。

失敗した場合についてはreject関数に、失敗時に通知するメッセージや値などを渡す。

な...何を言っているかわからねーと思うので以下に例を示す。

function hoge(value) {
  return new Promise((resolve, reject) => {
    if(value) {
        resolve("ふるえるぞハート! 燃えつきるほどヒ――――――ト!!")
    } else {
        reject("貧弱! 貧弱ゥ!")
    }
  })
}

まずはPromiseオブジェクトについて確認するため、上記の例では肝心の非同期処理は一切実装されていない。

hoge関数はreturn new Promiseとあるように、最終的にはPromiseオブジェクトを返すようになっている。そしてPromiseの中ではhoge関数の引数valueがtrueかfalseかでresolveとrejectに成功時と、失敗時のメッセージを与えている。

今の状態でconsole.log(hoge(true))hoge関数の結果を表示してみると以下のようになる。

f:id:oaths3:20200302225327p:plain

hoge関数からはPromiseオブジェクトが返されていることが分かる。

また、PromiseStatusがresolvedとなっている。これはhoge関数から返されたPromiseオブジェクトがresolved状態であることを示している。

console.log(hoge(false))とすればhoge関数のvalueは偽になり、PromiseStatusはrejectedとなる。

resolvedやrejectedはPromiseオブジェクトの状態を示すもの。

Promiseの状態

Promiseには以下3つの状態というものがある。 以下はMDNから引用。

  • pending: 初期状態。成功も失敗もしていません。

  • fulfilled(resolved): 処理が成功して完了したことを意味します。

  • rejected: 処理が失敗したことを意味します。

Promiseの中で定義した処理が成功したか失敗したかにより、pending状態だったPromiseオブジェクトがresolvedかrejectedのどちらかの状態に変化する。

Promiseオブジェクトを利用する

Promiseオブジェクトの定義方法を説明したが、今のままではhoge関数はPromiseオブジェクトを返すだけなので、成功時や失敗時の値を取得することができない。 そこでPromiseオブジェクトを利用するためのメソッドがthenメソッドやcatchメソッドになる。

thenメソッドについて

thenメソッドを使うとPromiseオブジェクトの結果を受け取ることができる。

promise.then(成功時処理、失敗時処理)

thenメソッドの第一引数には成功時に実行したい処理を記述し、第2引数には失敗時に実行したい処理を記述する事ができる。

hoge(true).then(
  // 第一引数に成功時の処理を書く
  response => {
    console.log(response);
  },
 // 第二引数に失敗時の処理を書く
  error => {
    console.log(error);
  }
);
// => ふるえるぞハート! 燃えつきるほどヒ――――――ト!!

上記の例ではhoge関数にtrueを渡して実行しているので、hoge関数はresolved状態のPromiseオブジェクトを返す。

返ってきたPromiseオブジェクトに対しthenメソッドを呼び出すことで、その結果を受け取ることができる。thenメソッドの引数には2つのコールバック関数が定義されている。どちらも単純に受け取った結果をコンソールに表示するようになっている。

Promiseオブジェクトがresolved状態なら、thenメソッドの第一引数に指定したコールバック関数が実行される。Promiseオブジェクトの結果をresponseとして受け取り、それをコンソールに表示している。

同様に、hoge関数にfalseを渡して実行すれば、hoge関数はrejected状態のPromiseオブジェクトを返し、thenメソッドの第二引数で指定したコールバック関数が、Promiseの結果をerrorとして受け取り、それをコンソールに表示する。

ちなみにresponseやerrorは任意の名前を付けて構わない。

catchメソッドについて

先程はPromiseオブジェクトの結果をthenメソッドで受け取り、thenメソッドの中で成功時と失敗時の処理を記述していたが、thenメソッドの中には成功時の処理だけを記述し、失敗時の処理をthenメソッドの中から切り離すことができる。

そのためにはcatchメソッドを利用する。

hoge(false)
  .then(
    response => {
      console.log(response)
    }
  )
  .catch(
    error => {
      console.log(error)
    }
  );
// => 貧弱! 貧弱ゥ!

上記のようにthenメソッドの後にcatchメソッドを呼び出し、catchメソッドの中で失敗時の処理を記述する。

hoge関数にfalseを渡して実行しているので、hoge関数はrejected状態のPromiseオブジェクトを返す。この場合thenメソッドは無視されcatchメソッドだけが実行される。

catchメソッドで失敗時の処理を切り分けることで、可読性が向上したり、処理を一箇所にまとめることができる。

Promiseチェーンについて

thenメソッドは何度でも繋げることができる。以下はMDNで使われている例。

doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
  console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);

上記の例ではthenが3回使われている。

doSomething関数はPromiseオブジェクトを返し、その結果が1つ目のthenに渡される。1つ目のthenの結果は2つ目のthenに渡され、2つ目のthenの結果は3つ目のthenに渡される。そして、いずれかの処理で失敗した場合には最後のcatchが呼ばれることになる。

ちなみにPromiseを使わないで上記のような処理を書くと以下のようになる。

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

これがいわゆるコールバック地獄というやつで非常に分かりづらい。

Promiseを使うと簡潔に書けてとっても嬉しいことが分かる。

まとめ

Promiseについてなんとなく理解することができた。例として記述したコードに非同期処理の部分が無いためイマイチメリットが伝わらないかもしれない。axiosなど使ってみるとナルホドとなることがあるかもしれません。

【Vue.js】コンポーネントのdata()について

はじめに

Vue.jsのコンポーネントでdataを定義する際に、公式サイトでは以下のような書き方が使われています。

Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

data: function() {}と書いていくのが通常なのかと思っていたところ、以下のような書き方もできると知りました。

Vue.component('button-counter', {
  data() {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
変わったのはdata: function() {}data() {}となっているところ。 しかし、なぜこの書き方ができるのか分からなかったので調べてみました。

結論

先に結論を書いてしまうと、ECMAScript 2015から省略記法が利用できるようになり、functionを書かずにメソッド(プロパティに関数を格納しているもの)を定義できるようになったため。 以下のリンク先の「メソッドの定義」部分に省略記法についての記載があります。 developer.mozilla.org

JavaScriptのプロパティに関数を格納する書き方

省略記法を用いない場合、オブジェクトのプロパティに関数を格納するには以下のように書きます。

obj = { hoge: function() { console.log("hogehoge") } };
obj.hoge();
//=> hogehoge

これが省略記法を用いると以下のように書けるようになりました。

obj = { hoge() { console.log("hogehoge") } };
obj.hoge();
//=> hogehoge
省略記法を使う場合には:(コロン)とfunctionを書く必要が無くなります。

ここで、もう一度Vueのコンポーネントの記述を見てみると、Vue.componentの第二引数にオブジェクトが渡されており、オブジェクトのプロパティとしてdataが定義されています。 したがって、data(){ }という書き方は省略記法を使ってdataプロパティに関数を格納していると分かりました。

// 省略記法を使ってdataプロパティに関数を格納している
Vue.component('button-counter', {
  data() {  },
})

まとめ

分かってしまうと単に省略記法で書いていただけという結論でしたが、調べる過程でJSの理解が少し深まった気がします。 VueやReactを使うにしても、やはりJSの基本を理解しておくことは大事だと感じました。

参考サイト

developer.mozilla.org

jp.vuejs.org

jp.vuejs.org