yn2011's blog

技術メモ

CSVファイル差分比較マニュアル

環境

やりたいこと

  • 2つのCSVファイルの差分比較をしたい
  • 差分がある場合は行単位ではなく列単位で分かるようにしたい
  • CSVファイル内のソートの差異や余分な行が含まれている等が原因で差分が発生している場合は直したい

以下、CSVファイルの差分比較を行う手順と注意事項を書いていく。

比較対象のCSVファイルを整形する

  • CSVファイルを比較可能な形に揃える作業
    • 自分の経験上、業務でCSVファイルの比較を行う場合は何らかの前処理(余分な行・列の削除、ソート)が必要なことが多い
  • シェルスクリプトで整形する
  • 正直Excelでもできるが、Excelで編集した途端に日付、ダブルクォート、真偽値(true, false)が勝手に大文字になる、先頭の0が消滅する等何らかの災いが発生するので使わないほうが賢明と思う
    • 比較する2ファイルともExcel通したら良いじゃんって話でもあるが、元データが意図せず破壊されることがある時点で何か気持ち悪い
    • 比較するファイルのサイズが大きい場合は実行効率の面でもシェルスクリプトが有利
      • 昔、2~3桁MBのCSVファイルの差分比較をしなければいけなくて、とりあえずExcelで開いたらExcelが重すぎて使えなくて焦ったことがあるが、シェルスクリプトで処理したらまったく問題にならなくて非常に助かった思い出がある

ソート

$ sort

補足

  • カラムが多すぎてソートに指定したい列が何番目か分からなかったりする場合のワンライナー
    • カラム名にカンマが含まれる場合は正規表現の改良が必要
    • もっとスマートなやり方がありそうな気がする(最後のカラムは表示されないし...)
cat hoge.csv | head -n 1 | sed -e 's/,/\n/g' | nl
     1  "都道府県コード"
     2  "都道府県名"
     3  "元号"
     4  "和暦(年)"

文字コード変換

  • Shift_JISだとターミナル上で日本語が文字化けする
# 文字コード判定
$ nkf -g hoge.csv
Shift_JIS
# UTF-8に変換(上書き)
$ nkf --overwrite -w hoge.csv

実際に比較する

git diff

  • こういうCSVファイルの場合
// test.csv
"1","2","3","4","5"
"6","7","8","9","10"
// test2.csv
"1","2","3","4","5"
"6","7","10","9","11"
  • 比較する(2行目の3列目と5列目に差異)
$ git diff --word-diff-regex="[^[:space:],]+" test.csv test2.csv
diff --git a/test.csv b/test2.csv
index bbe528b..e69d468 100644
--- a/test.csv
+++ b/test2.csv
@@ -1,2 +1,2 @@
"1","2","3","4","5"
"6","7"[-"8","9"-],"10",{+"9","11"+}
  • 確かに差分比較できているが、上記のようなケースだと2行目3・5列目に差異があることが明確に視認できない(気がするのは自分だけ?)

csvdiff

  • もっと良さげなのを探した

github.com

  • 3列目と5列目に差異が発生していることが明確に分かる
csvdiff test.csv test2.csv -o word-diff
# Additions (0)
# Modifications (1)
6,7,[-8-]{+10+},9,[-10-]{+11+}
  • また、このようにユニークキーを持つCSVファイルの場合は自動的にソート(比較行を選択)してくれる
// test3.csv
aaa, 1, 2, 3, 4, 5
bbb, 6, 7, 8, 9, 10
// test4.csv
bbb, 6, 7, 10, 9, 11
aaa, 1, 2, 3, 4, 5
$ csvdiff test.csv test2.csv -o word-diff -p 1
# Additions (0)
# Modifications (1)
bbb, 6, 7,[- 8-]{+ 10+}, 9,[- 10-]{+ 11+}
  • 比較したい列番号が予め分かっている場合は、その列だけで比較判定できる
$ csvdiff test.csv test2.csv -o word-diff -p 0 --columns 2
# Additions (0)
# Modifications (0)

まとめ

  • csvdiff便利そう

Map型の変数の命名について考えた

Map型の変数

ふと、Map型の変数の名前の命名って何がベストなんだろうと気になったのでパターンをまとめてみた。

ハンガリアン記法パターン

とりあえず変数名の末尾にMapをつけておくパターン。 例えば、社員コードと社員名の対応付けを格納するMapの場合はこんな感じだろうか。

var empCodeMap = new Map();
empCodeMap.set('123', '太郎');
empCodeMap.get('123');

型宣言が不要な言語の場合は命名の選択肢としてはアリかもしれない。 そうでないならあまり選択するメリットがないと思う。

KeyToValueパターン

キーと対応する値を変数名に含めるパターン。 シンプルで情報量が多いのが利点。私はいつもこの命名パターンを利用してMap型の変数を宣言する。

var empCodeToName = new Map();
empCodeToName.set('123', '太郎');
empCodeToName.get('123');

valueByKeyパターン

値を変数名の先頭に持ってきてキーを後に続けるパターン。 KeyToValueパターンと情報量は変わらないので好みの問題?

var nameByEmpCode = new Map();
nameByEmpCode.set('123', '太郎');
nameByEmpCode.get('123');

valueOfパターン

言語の構文によっては前置詞が自然に繋がるので良いのかもしれない。 例えばJavaScriptJavaだと不自然な上に情報量が減るので悪い命名だと思う。

var empCodeOf = new Map();
empCodeOf.set('123', '太郎');
empCodeOf.get('123');

参考

「プロを目指す人のためのRuby入門」を読んでRubyに入門した感想

プロを目指す人のためのRuby入門

プロを目指す人のためのRuby入門

  • 他言語経験者向けのRuby入門書
  • 現場で必要とされるRubyの知識を効率良く学べる

自分のスキル

感想

本の感想というよりはRubyを初めて学んで感じたことをジャンル別に整理してみた。

驚き

(少なくとも自分は)他の言語で見たことがない規則や構文

  • 変数を宣言するだけの構文はない
  • 末尾が?のメソッド名は返り値が真偽値、末尾が!は副作用ありを示す慣習
  • メソッド内で最後に評価された式が戻り値になる
  • メソッド宣言時や呼び出し時に引数のカッコを省略可能
  • ifの反対の意味を持つunless
  • シンボルの概念(JavaEnumと同じな気もする)
  • ifの条件式でローカル変数に代入
  • privateメソッドはレシーバーを指定できないという定義
  • aliasキーワード(便利に見えるけど混乱するだけな気もする)
  • クラスにアクセス修飾子が使えない

JavaScriptにも欲しい

  • キーワード引数(呼び出し部分だけ見て引数の意味が分かる)
  • &.演算子nilチェック

JavaScriptとは違う

  • メソッド呼び出し時の引数の過不足はエラー
  • ブロックの概念(クロージャ
  • 範囲オブジェクトがある

JavaScriptと似ている

  • 後付で何でも修正できる

忘れそう・難しい

  • 引数の最後がハッシュならハッシュリテラルの{}を省略可能
  • メソッド宣言時の引数の種類と並び順
  • Proc.newとラムダの違い
  • 二重コロンでモジュール内のクラス、クラス内の定数参照

例外クラスについて

  • ちょっと興味深かったのでJavaRubyの例外クラスを比較してみる
  • Javaだとコンパイル時の静的検査で検出される問題が、Rubyでは例外として用意されている

  • Ruby

f:id:pokuwagata:20190324222650p:plain (プロを目指す人のためのRuby入門 図9-3より引用)

  • Java f:id:pokuwagata:20190324222944p:plainJava本格入門 6-1-2 例外を表す3つのクラスより引用)

総括

  • (慣れの問題とは思うが)Rubyは省略記法や構文が独特かつ自由な印象があってすぐに忘れそう
    • その分他の言語と比べると、表現したいことに対してコードの量は少なく済むので慣れている人は書きやすいし読みやすいのかも
  • 静的型付がないので、コードの品質を保てないまま規模が大きくなると非常に辛そう(JavaScriptと同様な感じになる)

次はRails

  • Railsチュートリアル進行中(6章)
  • いきなりRails学び始める前に1冊Rubyの本を読んでおいて良かったと感じる
    • Rails独自のメソッドや変数が多すぎて辛いが結局はRubyで実装しているので)突き詰めて参照していけばRailsのコード読める

Rules of Hooksに違反していないのにHooks can only be called inside the body of a function componentエラーにハマった(と思った)話

環境

  • react 16.8.2
  • webpack 4.29.3

事象

  • webpack-dev-sereverで、Hooksを利用した下記のコードを実行するとHooks can only be called inside the body of a function componentエラーが発生

再現手順

  • 問題のコード(イメージ)
// ./App/index.tsx

import * as React from 'react';

const App = () => {
  const hooks = React.useState('hoge');

  return <span>hoge</span>;
};

export default App;
// ./index.tsx

import * as React from 'react';
import * as ReactDOM from 'react-dom';

import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));
<!-- ./index.html -->
<html>
  <body>
    <div id="root"></div>
    <script src="bundle.js"></script>
  </body>
</html>
  • webpack-dev-serverを利用してindex.htmlを開くとUncaught Invariant Violation: Hooks can only be called inside the body of a function componentエラー

    • 単純にビルドだけして開いてみたら発生しなかった(webpack-dev-serverが怪しい...?)
  • エラー自体はHooksの呼び出し位置が適切できないといった内容のようだが、この場合は特にそんなことはない(詳細はRules of Hooks参照)

  • こんなシンプルな構成なのになんで? 何が起きてる?

HtmlWebpackPluginのhtmlテンプレートが原因

  • 自分の環境ではwebpack-dev-serverを利用する際に HtmlWebpackPluginを使って動的にhtmlファイルを出力していた
  • なので、上記のindex.htmlをそのまま利用しているわけではなく、htmlテンプレートとして使っていた
  • おもむろにindex.htmlをブラウザで開いてみたら...
<html>
  <body>
    <div id="root"></div>
    <script src="bundle.js"></script>
    <script src="bundle.js"></script>
  </body>
</html>
  • このようにバンドルされたjsファイルへの参照が自動挿入されて2つになっていた

    • (webpack-dev-serverを利用しない時は上記のhtmlを使用しないので再現しなかった)
  • ひとまずhtmlテンプレートを修正したらエラーを吐かなくなって一安心

ReactDOM.renderを2回呼び出すとどうなるか

If the React element was previously rendered into container, this will perform an update on it and only mutate the DOM as necessary to reflect the latest React element.

ReactDOM – Reactより引用

  • ただしFunction Componentのrenderメソッド相当(返り値)以外の部分は普通に実行されるのでReact.useState('hoge')が2度評価されたことが原因っぽいなあと予想
  • しかし、ReactDOM.renderを2回続けて呼び出すコードを実行しても再現しなかった
  • ReactDOM.renderというよりbundle.jsを2回読み込んでいることが影響しているのかも
  • Hooks使わないコードだとエラーが発生しないので今までhtmlテンプレートに問題があることに気づけなかった...

VSCodeでTypeScriptを書くときに役立った拡張機能まとめ

1~2週間ほどVSCodeでTypeScript(+React)を書いていたので、その際に役に立った拡張機能をまとめ。

環境

TypeScript Importer

  • TypeScript Importer - Visual Studio Marketplace
  • 未インポートのモジュールを利用してコードを書き始めると自動的にインポートの宣言文を挿入してくれる
    • ただし、たまに意図しない動作をしたり、あるいは動作を期待していたら動作しなかったりすることがある気がする
  • VSCodeのコマンドパレットからAdd Importを実行すると、インポートするモジュールを選べる
    • 自分でインポートの宣言文を書く気にはなれないのでこれがけっこう便利

TypeScript Import Sorter

  • TypeScript Import Sorter - Visual Studio Marketplace
  • VSCodeのコマンドパレットからSort Importsを実行して、インポートの宣言文をソートできる
  • 設定すればファイル保存時に自動的ソートすることもできて便利("importSorter.generalConfiguration.sortOnBeforeSave": true

Move TS

Prettier

  • Prettier - Code formatter - Visual Studio Marketplace
  • コード整形。使ってみると、これなしの開発は考えられなくなる
  • 自分は元々使っていて、TypeScriptファイル保存時にもPrettierが自動的に実行されるように設定した

ES7 React/Redux/GraphQL/React-Native snippets

その他

  • lintについては、今後はTSLintではなくESLintがTypeScript対応を進めるという発表があり( TypeScript Roadmap: January - June 2019 · Issue #29288 · Microsoft/TypeScript · GitHub)しばらくは手を出さなくていいかなと思ってる
  • 自分は、ES5のコードベースでもPrettierを使うならESLintはno-unused-varとかno-unreachableぐらいで十分だと思っていて、TypeScriptについてはVSCodeが最低限の警告を出してくれる(no-unreachableは警告してくれないみたいだけど)のであまり困っていないというのもある

まとめ

  • そういうわけでTypeScript書いていきましょう

"Java 8 is required to run..."エラーが表示されVSCodeでApexのシンボル情報を参照できなくなった場合の対応

環境

事象

  • なぜかVSCodeでApexのシンボル情報を参照できなくなった(とても開発効率が落ちる

    • ↓これが出なくなった
      f:id:pokuwagata:20190123223417p:plain
      Apex(VSCode拡張)の詳細ページから引用
    • ついでにサイドバーのアウトラインも表示されなくなった
  • VSCode起動時にJava 8 is required to run. Download and install it from https://java.com/en/download/.というエラーが表示されるようになった

Java11が原因?

forcedotcom/salesforcedx-vscodeにissueが起票されていた github.com - Apex Language Server · forcedotcom/salesforcedx-vscode Wiki · GitHubに書いている通りにsalesforcedx-vscode-apex.java.homeにJava8用のパスを設定したら直った

  • $ java --versionしたらJava11.0.2だった
    • VSCode拡張のApexはJava8のみ対応とのことだった
  • 特に環境を更新した記憶はないけど、知らない内にJava11がインストールされていた...?

文字からUTF-8のバイトコードを調べるワンライナー

環境

文字からUTF-8バイトコードを調べるワンライナー

  • (全角ハイフン)を調べたい場合
$ echo -n 'ー' | iconv -f utf-8 | xxd -p
e383bc
  • -tを明示しない場合はUTF-8で出力と解釈される

ワンライナーの意味

出力するエンコーディング方式を指定する

$ echo -n 'ー' | iconv -f utf-8 -t utf-16 | xxd -p
feff30fc
$ echo -n 'ー' | iconv -f utf-8 -t sjis | xxd -p
815b

他の方法は

  • あるかもしれないです