LoginSignup
4
2

More than 3 years have passed since last update.

RxJSのfilterやmergeMapを活用してNEMのトランザクションデータをブラウザ上に表示する

Last updated at Posted at 2019-06-11

前回はnem2-sdkをブラウザに使用できるようにして、簡単な情報を取得してみました。
今回は特定のアカウントからトランザクションデータを取得してみます。

その時の肝となる技術がRxJSです。

RxJS を学ぼう #1 - これからはじめる人のための導入編
https://tech.recruit-mp.co.jp/front-end/rxjs-intro/

Catapultからデータを取ってきた場合、基本的には以下のような処理を経て画面上に出力します。

accountHttp
    .getAccountInfo(address)
    .subscribe(
        accountInfo => {
            $("#account_pubkey").text(accountInfo.publicKey);

        },
        err => console.error(err)
    );

accountHttpでノードに接続。getAccountInfoでアカウント情報を取りに行き、取れたらsubscribe内が処理されます。accountInfoに情報が入るので、適切な形に加工して画面に表示します。

では、取得したデータがリスト形式の場合はどんな方法で処理していくのでしょうか?
さきほどのソースコードは以下のような書式に書き換えることができます。

accountHttp
    .outgoingTransactions(originAccount)
    .subscribe(
        function(transactions){
            console.log(transactions);  
        },
        err => console.error(err)
    );

少し見慣れた形になりましたね。今回はトランザクションデータを取得したので出力されたデータが配列で入ってきますので、同一項目のデータにコンバートをかけていくには、中でさらにループ処理を施す必要があります。

accountHttp
    .outgoingTransactions(originAccount)
    .subscribe(
        function(transactions){
            transactions.some(function(tx){
                console.log(tx);    
            });
        },
        err => console.error(err)
    );

someの部分がループ処理になります。この内部にロジックを記述していくことで、どんな出力形式にでも対応していくことが可能です。
ただ、一括で変換をかけたいとき、あるいは処理対象でないレコードを除外したい場合は、RxJSのオペレータコマンドを利用して操作することが可能です。
以下のようにsubscribeの前にpipe()というセクションを作るとその中でmap、filter、などで一括操作が行えます。

accountHttp
    .outgoingTransactions(originAccount)
    .pipe(
        rxjs.operators.mergeMap((_) => _),
        rxjs.operators.filter((_) => _.type === nem.TransactionType.TRANSFER), 
        rxjs.operators.toArray(),
    )
    .subscribe(
        transactions => {
            $('#items2-ctrl ul').append('<li>' + transactions.recipient.address +  '</li>');
        },
        err => console.error(err)
    );

順に説明していきます。

rxjs.operators.mergeMap((_) => _),

mergeMapは配列[a,b,c] と格納されていたデータを[a],[b],[c]と置き換え、後続のオペレーションを適用しやすく変換します。

rxjs.operators.filter((_) => _.type === nem.TransactionType.TRANSFER),

filter により、 transaction内部のtypeがTRANSFERのものに絞り込み、次の処理に通します。

rxjs.operators.toArray(),

toArrayをかけると[a],[b],[c]を再び[a,b,c]という配列に戻します。
これらの操作により、subscribeへはTRANSFER以外のデータが除去されて届きます。

最後に指定namespaceIdを持つ送金の合計値を計算するサンプルを2例示しておきます。
最初はfilter内でやってしまう方法、もう一つはsubscribe内でやってしまう方法です。


//一つ目
const namespaceId = new nem.NamespaceId([3294802500,2243684972]); 
accountHttp
    .outgoingTransactions(originAccount)
    .pipe(
        rxjs.operators.mergeMap((_) => _),
        rxjs.operators.filter((_) => _.type === nem.TransactionType.TRANSFER),
        rxjs.operators.map(function(tx){

            var amount = 0;
            tx.mosaics.some(function(mosaic) {
                if (mosaic.id.toString() == namespaceId.toString()) {
                    amount = mosaic.amount.compact() / Math.pow(10, divisibility);
                    return true;
                }
            });
            return amount;
        }),
        rxjs.operators.toArray(),
        rxjs.operators.map((_) => _.reduce((a, b) => a + b, 0))
    )
    .subscribe(
        total => {
            $('#account_transfer').text(total);
        },
        err => console.error(err)
    );


//二つ目
accountHttp
    .outgoingTransactions(originAccount)
    .pipe(
        rxjs.operators.mergeMap((_) => _),
        rxjs.operators.filter((_) => _.type === nem.TransactionType.TRANSFER),
        rxjs.operators.toArray(), // Add all mosaics amounts into one array
    )
    .subscribe(

        _ => {

            var amount = 0;
            _.some(function(tx) {

                tx.mosaics.some(function(mosaic) {
                    if (mosaic.id.toString() == namespaceId.toString()) {
                        amount += mosaic.amount.compact() / Math.pow(10, divisibility);
                        return true;
                    }
                });

            });
        },
        err => console.error(err)
    );

次回はnem2-sdkを利用したアカウント作成について説明します。

htmlサンプル
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0">
<title>011_account_sample</title>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
<script src="nem2-sdk-0.11.6.js"></script><!-- Catapult Cow -->
<script>
$(function() {
    const nem = require("/home/ec2-user/node_modules/nem2-sdk");
    const NODE_URL = "http://13.114.200.132:3000"; // nf-testnet
    const accountHttp = new nem.AccountHttp(NODE_URL);
    const originPublicKey = 'AC1A6E1D8DE5B17D2C6B1293F1CAD3829EEACF38D09311BB3C8E5A880092DE26';
    const originAccount = nem.PublicAccount.createFromPublicKey(originPublicKey, nem.NetworkType.MIJIN_TEST);

    const recipientAddress = 'SB2Y5ND4FDLBIO5KHXTKRWODDG2QHIN73DTYT2PC';
    const address = nem.Address.createFromRawAddress(recipientAddress);

    const divisibility = 6; // Replace with mosaic divisibility

    $('#account_address').text(recipientAddress);
    $('#account_pubkey').text(originPublicKey);

    accountHttp
        .outgoingTransactions(originAccount, new nem.QueryParams(100))
        .pipe(
            rxjs.operators.mergeMap((_) => _), // Transform transaction array to single transactions to process them
            rxjs.operators.filter((_) => _.type === nem.TransactionType.TRANSFER), // Filter transfer transactions
        )
        .subscribe(
            transactions => {
                console.log(transactions.message.payload);

                $('#items2-ctrl ul').append('<li>' + transactions.recipient.address +  '</li>');
            },
            err => console.error(err)
        );

    const namespaceId = new nem.NamespaceId([3294802500,2243684972]); 
    accountHttp
        .outgoingTransactions(originAccount, new nem.QueryParams(100))
        .pipe(
            rxjs.operators.mergeMap((_) => _),
            rxjs.operators.filter((_) => _.type === nem.TransactionType.TRANSFER),
            rxjs.operators.map(function(tx){

                var amount = 0;
                tx.mosaics.some(function(mosaic) {
                    if (mosaic.id.toString() == namespaceId.toString()) {
                        amount = mosaic.amount.compact() / Math.pow(10, divisibility);
                        return true;
                    }
                });
                return amount;
            }),
            rxjs.operators.toArray(),
            rxjs.operators.map((_) => _.reduce((a, b) => a + b, 0))
        )
        .subscribe(
            total => {
                $('#account_transfer').text(total);
            },
            err => console.error(err)
        );

});
</script>
</head>
<body>
    <div class="container">

        <h1>011_account_transfer</h1>
        <div id="items-ctrl">
            <ul>
                <li>[ADDRESS]:<span id="account_address"></span></li>
                <li>[PUBLIC_KEY]:<span id="account_pubkey"></span></li>
                <li>[OUTGOING_SUM]:<span id="account_transfer"></span></li>
            </ul>
        </div>

        <h1>transactions</h1>
        <div id="items2-ctrl">
            <ul>
            </ul>
        </div>

    </div>
</body>
</html>

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2