Quantcast
Browsing Latest Articles All 31 Live
Mark channel Not-Safe-For-Work? (0 votes)
Are you the publisher? or about this channel.
No ratings yet.
Articles:

C++ Builder XE4 > TEdit > 右欄のTEditの項目と同じkeyを持つ左欄のTEdit項目の値をvalue値に書換える > 二つの実装 (配列, TPanel上のTEdit探索)

動作環境
C++ builder XE4

やりたいこと

  • 左欄に(key, value)のTEditがある
  • 右欄に(key, value)のTEditがある
  • ボタン押下
    • 右欄の(key, value)をもとに左欄のkeyが同じTEditを探し
    • その値をvalueで書換える

実装内容

二種類にて実装した

  • A. 配列の使用
    • 利点: keyを見つけたときに対応するvalueのTEditが確定する
    • 欠点: 配列を用意する必要がある
  • B. コンポーネント名からKeyを探し、FindComponentでValueを探す
    • 利点: 配列を事前に用意する必要がない
      • 項目数増加の時のミスを抑止
    • 欠点: ルールに則った(一種類の)名前のコンポーネントだけを処理対象とする
      • 拡張可能ではあるが

実装

Unit1.h
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.Grids.hpp>
#include <Vcl.ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE で管理されるコンポーネント
    TPanel *PNL_left;
    TEdit *Device_L01;
    TLabel *Label1;
    TEdit *Item_L01;
    TLabel *Label2;
    TEdit *Device_L02;
    TEdit *Item_L02;
    TEdit *Device_L03;
    TEdit *Item_L03;
    TEdit *Device_L04;
    TEdit *Item_L04;
    TPanel *PNL_right;
    TEdit *Device_R01;
    TLabel *Label3;
    TEdit *Item_R01;
    TLabel *Label4;
    TEdit *Device_R02;
    TEdit *Item_R02;
    TEdit *Device_R03;
    TEdit *Item_R03;
    TEdit *Device_R04;
    TEdit *Item_R04;
    TButton *B_toLeft1;
    TButton *B_toLeft2;
    void __fastcall B_toLeft1Click(TObject *Sender);
    void __fastcall B_toLeft2Click(TObject *Sender);
private:    // ユーザー宣言
    static const int kNumDevice = 4;  //
    TEdit *m_deviceLefts[kNumDevice];  // replaceValue_arrayのKeyコンポーネント
    TEdit *m_itemLefts[kNumDevice];  //  replaceValue_arrayのValueコンポーネント
    void __fastcall replaceValue_array(String keystr, String valstr);
    void __fastcall replaceValue_findOnPanel(TPanel *pnlPtr, String keystr, String valstr);
public:     // ユーザー宣言
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    // Left Items
    //   replaceValue_array()にて使用
    m_deviceLefts[0] = Device_L01;
    m_deviceLefts[1] = Device_L02;
    m_deviceLefts[2] = Device_L03;
    m_deviceLefts[3] = Device_L04;
    m_itemLefts[0] = Item_L01;
    m_itemLefts[1] = Item_L02;
    m_itemLefts[2] = Item_L03;
    m_itemLefts[3] = Item_L04;
}
//---------------------------------------------------------------------------
// イベント
void __fastcall TForm1::B_toLeft1Click(TObject *Sender)
{
    // keystr: 探索のキー
    // valstr: 更新内容
    //                 keystr,           valstr
    replaceValue_array(Device_R01->Text, Item_R01->Text);
    replaceValue_array(Device_R02->Text, Item_R02->Text);
    replaceValue_array(Device_R03->Text, Item_R03->Text);
    replaceValue_array(Device_R04->Text, Item_R04->Text);
}
void __fastcall TForm1::B_toLeft2Click(TObject *Sender)
{
    TPanel *pnlPtr = PNL_left;  // 対象TPanel

    // keystr: 探索のキー
    // valstr: 更新内容
    //                               keystr,           valstr
    replaceValue_findOnPanel(pnlPtr, Device_R01->Text, Item_R01->Text);
    replaceValue_findOnPanel(pnlPtr, Device_R02->Text, Item_R02->Text);
    replaceValue_findOnPanel(pnlPtr, Device_R03->Text, Item_R03->Text);
    replaceValue_findOnPanel(pnlPtr, Device_R04->Text, Item_R04->Text);
}
//---------------------------------------------------------------------------
// private function
//
void __fastcall TForm1::replaceValue_array(String keystr, String valstr)
{
    // 方法A. 配列を使う

    for(int idx=0; idx < kNumDevice; idx++) {
        if (m_deviceLefts[idx] == NULL) {
            continue;
        }
        if (m_deviceLefts[idx]->Text != keystr) {
            continue;
        }
        m_itemLefts[idx]->Text = valstr;
    }
}
//
void __fastcall TForm1::replaceValue_findOnPanel(TPanel *pnlPtr, String keystr, String valstr)
{
    // 方法B. コンポーネント名からKeyを探し、FindComponentでValueを探す

    static const String kString_keyComponentName = L"Device"; // 対象となるTEditのコンポーネント名に含まれる文字列
    static const String kPrefix_key = L"Device";  // Keyコンポーネント名のプリフィックス
    static const String kPrefix_value = L"Item";  // Valueコンポーネント名のプリフィックス

    for(int idx=0; idx < pnlPtr->ControlCount; idx++) {
        // 1. 対象Keyコンポーネントの探索
        TEdit *devPtr = dynamic_cast<TEdit*>(pnlPtr->Controls[idx]);
        if (devPtr == NULL) {
            continue;
        }
        //
        //   同じルールのコンポーネント名か (例: Device_XXX not Item_XXX)
        if (devPtr->Name.Pos(kString_keyComponentName) == 0) {
            continue;
        }
        //
        if (devPtr->Text != keystr) {  // 同じ項目のものだけ
            continue;
        }

        // 2. 対象Valueコンポーネントの書換え
        String itmCmpntNam = StringReplace(devPtr->Name, kPrefix_key, kPrefix_value, TReplaceFlags()<<rfReplaceAll);
        TEdit *itmPtr = (TEdit *)FindComponent(itmCmpntNam);
        if (itmPtr == NULL) {
            continue; // not found
        }
        itmPtr->Text = valstr;
    }
}
//---------------------------------------------------------------------------

実行例

ソフト起動直後
2019-03-05_10h26_51.png

二つのボタンのいずれかを押した後
2019-03-05_10h27_07.png

会社のPythonスクリプト「データベース読み込み & 情報付与」を高速化したお話。

Introduction

ここに以下のような先人の方が作成したPython関数があります。あるとき、これを高速化してほしいとの依頼がありました。さてあなたならどうするでしょうか。ちなみにこの関数はforループによって複数回呼び出されており、その実行時間は135s程度でした。この待ち時間を短縮せよ、というのが今回のミッションです。

pre_improve.py
 def func(hogelist, dblines):
    annotation = ''
    flag = 0
    for name in hogelist:
        for line in dblines:
            dbname = line.split(sep='\t')[0]
            dbvalue = line.split(sep='\t')[7].replace(' ', '_')
            if name == dbname:
                if dbvalue != '':
                    if flag > 0:
                        annotation = annotation + ','
                    annotation = annotation + dbvalue
                    flag = 1
    result = ','.join(list(set(annotation.split(sep=','))))
    if result == '':
        result = '-'
    return(result)

この関数を呼び出しているmainはこんな感じです。

main.py
'''
    色々な処理
'''

with open('db.tsv', 'r') as f:
    dblines = f.readlines()

for hogelist in hogelists:

    result = func(hogelist, dblines)

'''
    色々な処理
'''

処理概要

dblinesはとあるデータベースから読み込んだものです。
この関数はhogelistのある要素をnameと呼称し、それがdblinesのある一行lineをsplitしたもののうちの0番目の要素が一致するかを判定します。
もし一致すれば、splitしたうちの7番目の要素を、そのnameに付随する情報として、annotationに付与します。
最後に、annotationのうち、重複するものがあればそれをsetを用いて除去したものをresultとして返します。もし、元からresultが空文字であれば、データベースに該当なしという意味を込めてハイフンを代入しています。

高速化

データベースをリスト型から辞書型へ変更する

以下のように関数を書き換えてみます。

post_improve_01.py
def func(hogelist, dblines):

        annotation = ''

        # set dbdict{dbname:dbvalue}
        dbdict = {}
        for line in dblines:
            dbname = line.split(sep='\t')[0]
            dbvalue = line.split(sep='\t')[7].replace(' ', '_')
            dbdict.setdefault(dbname, []).append(dbvalue)

        # set annotation
        for name in hogelist:
            for key in dbdict:
                if name == key:
                    annotation = dbdict[key]

        result = ','.join(list(set(annotation)))
        if result == '':
            result = '-'
        return(result)

lineをsplitしたときに、0番目の要素は同じですが、7番目の要素が違うものがある場合があります。それを辞書型のsetdefaultメソッドとappendを用いて、登録してあげています。この方法はこちらのページから勉強させていただきました。このアルゴリズム改善のおかげで、実行時間は119sまで短縮されました。

データベースのやりくりをforループの外側に持っていく

すでにお気付きかと思いますが、先の辞書dbdictは作成された後、この関数内では値の更新などは一切されていません。なので、この関数が複数回呼び出されるごとに辞書を一から作り直すのではなく、辞書を作成する外部関数を新たに作成し、その結果を参照する形にしましょう。
以下のように新しい関数を作り、既存の関数も書き換えます。

post_improve_02
# make dictionary
def make_dbdict(dblines):

    dbdict = {}
    for line in dblines:
        dbname = line.split(sep='\t')[0]
        dbvalue = line.split(sep='\t')[7].replace(' ', '_')
        dbdict.setdefault(dbname, []).append(dbvalue)
    return(dbdict)

def func(hogelist, dbdict):
    annotation = ''

    # set annotation
    for name in hogelist:
        for key in dbdict:
            if name == key:
                dbdict[key] = set(dbdict[key])
                dbdict[key].discard('')
                annotation = dbdict[key]

    result = ','.join(list(annotation))
    if result == '':
        result = '-'

    return(result)

そしてmainも以下のように書き変わります。

main.py
'''
    色々な処理
'''

with open('db.tsv', 'r') as f:
    dblines = f.readlines()

dbdict = make_dbdict(dblines)

for hogelist in hogelists:

    result = func(hogelist, dbdict)

'''
    色々な処理
'''

これでhogelistsの要素数の数だけ繰り返されていた辞書作成処理が1回になりました。このアルゴリズムの見直しで、この実行時間が0.54sまで縮みました。

@ lru_cacheデコレーターで処理をさらに高速化

アルゴリズムの見直しはここまで。ここから先は関数の戻り値をキャッシュとして記憶させることで実行時間を短縮させるという、少しディープなやり方をします。そのためには以下のモジュールをインポートします。

from functools import lru_cache

このモジュールの機能の詳細はこちらの記事こちらのブログを参照してください。

時間を少しでも短縮したい関数の前に

@lru_cache(maxsize=1024)

と、main関数の最後にメモリ解放のおまじない

func.cache_clear()

をつけます。するとこれだけで実行時間が0.37sまで短縮されました。

結論

今回は、すでに他の方が作成したPythonスクリプトを高速化するというミッションについてご説明しました。アルゴリズムの見直しから、Pythonの便利モジュールまで、あらゆる手を尽くしての高速化するという今回のミッション。結果として300-400倍高速化することに成功しました。
この記事が皆様の参考になれば幸いです。

【WinActor】ディクショナリやデータ一覧の代替案

WinActorでディクショナリやデータ一覧が使えずお困りの方は、参考にしていただけると幸いです。

ファイルパスやセルの位置などをノードに直接指定するのは、セルの位置が変わった時に変更の手間と変更漏れリスクがあるので、Excelで設定マスタを外出しにして、初期処理として読み込んだ値を変数に格納する運用にしています。
UiPathのディクショナリのような運用がWinActorでもできることを期待していましたが、残念ながら実現できないようです。
データ一覧に設定マスタをインポートしても、変数に格納される訳ではありませんでしたので、別の方法で作成しました。

■用意する変数
・設定マスタパス
・設定マスタ列
・設定マスタ行
・設定マスタセル位置
・処理結果
・設定マスタのKey列
※少し手間ですが、最初だけですので頑張りましょう!

1.設定マスタを用意する
下図のようなKey-Valueとなるマスタを用意する。
設定マスタ.png

2.Key列を1つずつ変数に登録する。
少し手間ですが、最初だけですので頑張りましょう!

3.設定マスタを選択
使用ノード:選択ボックス
・ファイル選択結果:設定マスタパス

4.設定マスタ選択判定
使用ノード:分岐
条件:設定マスタパス <> 空白
※空白だった場合はシナリオ停止ノードを入れました。

5.設定マスタ列初期化
使用ノード:変数値設定
・変数名:設定マスタ列
・値:Value列(例ではDを設定)

6.設定マスタ行初期化
使用ノード:変数値設定
・変数名:設定マスタ行
・値:ヘッダを除く一番上の行(例では3を設定)

7.設定マスタセル位置初期化
使用ノード:07_文字列操作\03_連結\文字列の連結(2つ)
・文字列1:設定マスタ列
・文字列2:設定マスタ行
・連結結果:設定マスタセル位置

8.Valueの取得
使用ノード:18_Excel関連\12_書式\Excel操作(値の取得)
・ファイル名:設定マスタパス
・シート名:
※空白だとアクティブなシートが対象になります
・セル位置:設定マスタセル位置
・格納先変数:Keyとして登録した変数

9.次の行へ
9-1.設定マスタ行カウントアップ
使用ノード:カウントアップ
・計算結果:設定マスタ行
・加算値:1

9-2.設定マスタセル位置設定
使用ノード:07_文字列操作\03_連結\文字列の連結(2つ)
・文字列1:設定マスタ列
・文字列2:設定マスタ行
・連結結果:設定マスタセル位置

8と9を設定マスタの最後まで繰り返す
※9は設定マスタに登録している分だけ必要なので、サブルーチン化して呼び出す形にしたほうが便利です。

9を用意した利点は、開発中に、設定マスタの見直し(行の追加、削除)があった場合に、流動的に対応できることです。
セル位置を直接指定してしまうと、場合によっては全行ズレてしまうことになりかねません。

10.設定マスタを閉じる
使用ノード:18_Excel関連\01_ファイル操作\Excel操作(保存なしで閉じる)
・ファイル名:設定マスタパス
・実行結果:処理結果

以上が、WinActorによるディクショナリやデータ一覧の代替案でした。
しつこいようですが、最初の登録さえしてしまえば、後はとても楽になります。

sortした辞書型を、辞書型に戻す方法

はじめに

辞書型をソートした場合としなかった場合で関数の戻り値の型が変わってしまうという問題に当たったのでその解決策を書いておきます。

解決策

Python 3.6.1

dic_sort.py
#PEP8に準拠
dic = {'A': 50, 'B': 20, 'C': 80, 'D': 10, 'E': 100}
list = sorted(dic.items(), key=lambda x: x[1], reverse=True)
dic.clear()
dic.update(list)

print(list)
print(dic)

結果

terminal
[('E', 100), ('C', 80), ('A', 50), ('B', 20), ('D', 10)]
{'E': 100, 'C': 80, 'A': 50, 'B': 20, 'D': 10}

解説

updateメソッドは(key,value)のリストを引数にできるので、dicをソートした戻り値が入っている変数listをclearされたdicに追加することでソートされた順のまま辞書型に戻すことができました。
注意点としては、(key,value)のリストを追加してもソートされた順が反映されるわけではありませんので、clearを行う必要があります。

参考にさせていただいたサイト

note.nkmk.me https://note.nkmk.me/python-dict-add-update/

Pythonの辞書についてさらっと

はじめに

Pythonをほとんど使ったことがなかったのですが,Pythonで辞書(dict型)を使う必要があり,少し勉強しました.
備忘録として残します.よかったらご活用します.
また,学びたてなので気になる点がございましたらご指摘いただけると幸いです.

辞書とは

雑に言うと,文字列(key)と数値(value)がセットになったもの.
Pythonドキュメントでは

辞書は キー(key): 値(value) のペアの集合であり、キーが (辞書の中で)一意でければならない
と説明されている.

辞書の生成

キーと値で生成

generate_dictionary
dic = {'key1': val1, 'key2': val2}

空の辞書の生成

generate_empty_dictionary
dic = {}

辞書の追加

add_dictionary
dic['key1'] = val1

コメントで教えていただいた事だが,スペース,改行区切りの文字列(str型)と数値(int型)を辞書に変換するために,以下のようにすることができる.

str_to_dict
dic = {}
str1 = ["hello", "world"]
val1 = [1, 2]
dic = dict(zip(str1, val1))

辞書の値の変更

change_value
dic['key1'] = val2

辞書の出力

0からnumまで出力する場合は以下.

output_dictionary_1
for i in range(num):
    print(dic[i])

辞書を全て出力する場合は以下.

output_dictionary
for t in dic.items():
    print(t)

ソート

いろんなページを参考にしたので,参考は後述する.

keyで昇順

key_sort_ascending_order
dic_s = sorted(dic.items())

keyで降順

降順にするためにはreverse=Trueをつけると良い.

key_sort_descending_order
dic_s = sorted(dic.items(), reverse=True)

valueで昇順

valueでソートするためにはkey=lambda x:x[1]をつけると良い.

value_sort_ascending_order
dic_s = sorted(dic.items(), key=lambda x:x[1])

valueで降順

key_sort_descending_order
dic_s = sorted(dic.items(), reverse=True, key=lambda x:x[1])

key-valueで昇順-昇順

key-valueの両方でソートするためにはkey=lambda x:(x[1],x[0])をつけると良い.

key-value_sort_ascend-ascend
dic_s = sorted(dic.items(), key=lambda x:(x[1], x[0]))

key-valueで降順-降順

key-value_sort_descend-descend
dic_s = sorted(dic.items(), reverse=True, key=lambda x:(x[1], x[0]))

key-valueで昇順-降順

これは,調べてもなかなか出てこなかったので,少し上のlambda式を変更して作成した.
key-valueのソートで昇順と降順を混合するために,ここではkey=value x:(-x[1], x[0])とした.
ソートを2回連続で使う方法もあり,そのためには後述するlistdictに変換する方法が必要であった.

key-value_sort_ascend-ascend
dic_s = sorted(dic.items(), key=lambda x:(-x[1], x[0]))

key-valueで降順-昇順

上で述べた昇順-降順ソートを反転することでできた.

key-value_sort_ascend-ascend
dic_s = sorted(dic.items(), reverse=True, key=lambda x:(-x[1], x[0]))

ソートしてlistになってほしくない場合

以上で例に挙げた書き方では,ソート後にはlist型になっておった.これをdict型にするためには以下の方法がある.

list_to_dict_1
dic = {k: v for (k, v) in dic_s.items()}
list_to_dict_2
dic = dict(dic_s.items())

以下のようにすると,ソートと変換を同時に行うことができた.

sort_to_dict
dic = dict(sorted(dic.items()))

参考

おわりに

今回はさらっと撫でるようにしか書かなかったのですが,また使う機会があればその都度更新しようと思います.

TextFieldに入力した値を辞書型で保存

TextFieldに入力した値を辞書型で保存して、アプリを再度開いても保存した値が入力されてる状態のアプリを作らないといけないのですが、どのように作ればいいのかがわかりません。

値を読み込み、保存するときに何をつかうか
- 辞書で保存する際にkeyは"fruit"と"vege"の二種類が必要
- 保存時のキーと辞書のキーを混同しないように
作る際にこの条件にそってつくりたいです。

Pythonの多次元Dictionary(辞書型)のキー、値の検索・存在確認

Alexaスキルを作っていると、多次元の辞書型を扱うことが多いのですが、

キーの存在確認をする際に、2次元以上、多次元のケースは扱っている文献が見つからなかったので、メモ程度ですが残しておきます。

辞書型の文法については、巷のページで十分カバーされていますので、省略します。

dict.py
#辞書型の宣言
dict = { 'key1' : 'value1' , 'key2' : 'value2' }
dict['key1'] = { 'key1_1' : 'value1_1' , 'key2_1' : 'value2_1' }

'''
#構成はこんな感じ
dict{
    'key1': {
        'key1_1' : 'value1_1',
        'key2_1' : 'value2_1'
    },
    'key2':'value2'
}
'''
print( 'key1_1' in dict )
#2次元目にあるキーは見つからない (False)

print( 'key1_1' in dict['key1'] )
#これで見つかる(True)

dict_key1 = dict['key1']
#2次元目を切り出しても良い
print( 'key1_1' in dict_key1 )
#これでも見つかる(True)

print( 'value2_1' in dict_key1.values() )
#Valueも同じく見つかる(True)

print( 'key2' in dict_key1 )
#当然ながら1次元目のキーは見つからない(False)

pandasのセルのなかにdictionaryが!

ある日pandasのread_jsonで辞書形式のデータを読み込んでて気づいたもの。

1.起

こういうデータを

 [{"label":"20/00","x":297.0729064941406,"y":-385.3456726074219,"id":"145", "attributes":{"time":"1530"},"color":"rgb(153,153,153)","size":16.59930992126465},{"label":"10/04","x":4398.1484375,"y":-2204.3076171875,"id":"2692","attributes":{"time":"2460"},"color":"rgb(153,153,153)","size":4.0},・・・略・・・}]

こう読み込んだ。

df = pd.read_json("20153_nodes.json",orient="records")

ふとdfで出力してみるとセルの中に辞書っぽいものが!

image.png

attribute…ほんとは辞書の中の値を取り出したいのに。
どうも辞書の中に辞書形式でデータが入ってたようで。

2.承

まあstr⇒置換しちゃえばいいやと思ったらできない!(NaNになってしまう)
辞書で入っている!

df['attributes'] = df['attributes'].str.replace("{'time", "")

image.png

3.転

ネット上でそれらしき情報があったので、参考に。しかし意味不明の日本語。結果として無事にできました。

df["attributes"] = df["attributes"].apply(pd.Series)

image.png

4.結

pandasってネスト状態の辞書もちゃんとそのまま読み込むんだなーと感心。
今回はネスト内で値が1つしかなかったけど、複数あっても上記方法で分解してから、
mergeかjoinで連結すればもとのdfにひっつけられますね。

多重Dictionaryをつくりたかった

C#は素人なので定石がわかりません。

こういう連想配列があったとしましょう。

PHP
$datetimes = [
    'past' => [
        'yesterday' => new DateTime('yesterday'),
        'lastweek' => new DateTime('last week'),
    ],
    'future' => [
        'tomorrow' => new DateTime('tomorrow'),
        'nextyear' => new DateTime('next year'),
    ]
];

いや、こんな意味のわからない配列なんて作らねえよという抗議は全くもってその通りなのですがそこはスルーします。
実際はAPIに飛んできたリクエストをユーザ単位にまとめるみたいな処理をするところで、何個飛んでくるかわからないという仕様です。

PHPで書くとこんな感じでさくっとできます。

PHP
$datetimes = [];
foreach($request as $v){
    $datetimes[$v['tense']][$v['key']] = new DateTime($v['value']);
}

一瞬で書けてとってもらくちん。

ということで、この連想配列をC#に移植しようとしたのですが、どうもこのような構造について話をしているところがほとんど見当たりませんでした。
多重連想配列はC#では禁忌なのだろうか。

連想配列はDictionaryを使えばいいみたいですが、これは基本的に値がひとつです。
NameValueCollectionというのが目的に近かったのですが、これは値がstring固定みたいです。なぜ。
Microsoft.Experimental.Collectionsは名前が既に危ない。

試しにDictionaryの中にDictionaryを突っ込んでみたら普通にいけました。

    var datetimes = new Dictionary<string, Dictionary<string, DateTime>>();

    var list1 = new Dictionary<string, DateTime>();
    list1.Add("yesterday", new DateTime("yesterday"));
    list1.Add("lastweek", new DateTime("last week"));

    var list2 = new Dictionary<string, DateTime>();
    list2.Add("tomorrow", new DateTime("tomorrow"));
    list2.Add("nextyear", new DateTime("next year"));

    datetimes.Add("past", list1);
    datetimes.Add("future", list2);

ただ、この構造だとlist1などの中間変数が必要になってしまいます。
一気にdatetimes["past"].Add("yesterday", new DateTime("yesterday"));みたいなことはできないの?

    var datetimes = new Dictionary<string, Dictionary<string, DateTime>>();

    // The given key "past" was not present in the dictionary
    datetimes["past"].Add("yesterday", new DateTime("yesterday"));

    // Object reference not set to an instance of an object
    datetimes.Add("past", null);

    // An item with the same key has already been added
    datetimes.Add("past", new Dictionary<string, DateTime>{{"yesterday", new DateTime("yesterday")}} );
    datetimes.Add("past", new Dictionary<string, DateTime>{{"lastweek", new DateTime("last week")}} );

    // これはOK
    datetimes.Add("past", new Dictionary<string, DateTime>{{"yesterday", new DateTime("yesterday")}} );
    datetimes["past"].Add("lastweek", new DateTime("last week"));

Add自体はできるみたいですが、親Dictionaryのキーの有無によって動作を変えなければなりません。
キーが存在しない場合のみキーをAddし、その後はその中身だけAddしないといけないようです。
なんというかとても面倒。

もっとなんかマシな方法ってのがあるんだろうけどよくわかりませんでした。
そもそもC#ではどういう作りにするのが定石なんだろうか。

DictionaryのValue(値)をListにする

DictionaryのValue(値)をListにする

DictionaryのKeyをstringに、ValueをListにしたかったので以下のように実装しました。

冷蔵庫に入ってるものを種類別に格納しておくようなものを想定。
Listを定義してからDictionaryにAddするのはコードも長くなるのでなるだけ簡潔に追加したいなと思って以下のようにしました。

ローカル以外では初期化できなかった(UnityだけでなくC#も?)ので、publicとして使いたいものを宣言と同時に初期化で値を入れておくことはできないのかな、と思いこういうコードに。
メソッド内の初期化を忘れており、しばらく
NullReferenceException: Object reference not set to an instance of an object
で怒られました。

宣言と追加

Dictionary_value_list.cs
usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassDictionary_value_list:MonoBehaviour{publicDictionary<string,List<string>>MapFridge;publicvoidSetmap(){//ローカルで初期化MapFridge=newDictionary<string,List<string>>();MapFridge.Add("野菜",newList<string>(){"きゅうり","トマト","玉ねぎ"});MapFridge.Add("果物",newList<string>(){"みかん","パイナップル","いちご"});MapFridge.Add("魚",newList<string>(){"さけ","サバ","はまち"});}}

取得と表示

Dictionary_value_list.cs
//Keyを取得して表示foreach(stringsinMapFridge.Keys){Debug.Log(s);}

コンソールでの表示
key.PNG

Dictionary_value_list.cs
//Valueを取得して表示foreach(List<string>liinMapFridge.Values){foreach(stringsinli){Debug.Log(s);}

コンソールでの表示
value.PNG

Dictionary_value_list.cs
//KeyとValueのPairを取得して表示foreach(KeyValuePair<string,List<string>>iteminMapFridge){Debug.Log(item.Key+"はこれがあります");foreach(stringsinitem.Value){Debug.Log(s);}}

コンソールでの表示
both.PNG

ValueはListなのでさらにその中の要素に対してforeach。
Debug.Logはデフォルトで改行されちゃうので、同一行に出力したいときにfor文とか回してしまうと必ず改行されちゃうのが不便。
どうしても改行したくないときは、stringに連結していって最後に表示するとか。

一要素だけ取り出すときにはKeyを指定するしかなさそう。

MacBook Pro (16-inch, 2019)で辞書.app(Dictionary.app)の英和/和英辞典が使えなくなった話。

表題の通りの現象に遭遇して何とか解決したので、その記録です。

辞書.app(Dictionary.app)の環境設定から「ウィズダム英和辞典 / ウィズダム和英辞典 (日本語-英語)」を有効にしましたが、検索しても何も見つからない状態になりました。
スクリーンショット 2019-11-27 午後2.49.59.png
調べると同様の現象が発生している模様。
The macOS dictionary app cannot add new build in dictionaries - Apple Community

これによると、 /System/Library/AssetsV2/com_apple_MobileAsset_DictionaryServices_dictionaryOSXフォルダにある com_apple_MobileAsset_DictionaryServices_dictionaryOSX.xmlファイルに辞書データのダウンロードURLがあるので、ここからダウンロードして.dictionaryパッケージを ~/Library/Dictionariesフォルダにコピーしたら直った、とのこと。

これを参考に com_apple_MobileAsset_DictionaryServices_dictionaryOSX.xmlファイルからウィズダム英和/和英辞典の辞書データのURLを探してみます。

<dict><!-- ... --><key>DictionaryPackageDisplayName</key><string>ウィズダム英和辞典 / ウィズダム和英辞典</string><!-- ... --><key>__BaseURL</key><string>http://appldnld.apple.com/osxassets/058-55450-2016008023-8A4D782E-661F-11E6-B483-D75733D2D062/</string><key>__CanUseLocalCacheServer</key><true/><key>__InstallWithOS</key><true/><key>__RelativePath</key><string>com_apple_MobileAsset_DictionaryServices_dictionaryOSX/1a184f41ee27bbb203ba596a25c8ea104c13d98f.zip</string></dict>

ファイル内にはこのような構造があり、 __BaseURL__RelativePathを繋げると辞書データのダウンロードURLになりそうです。ところが、同じ辞書について同じような構造が5つありました。 __BaseURL__RelativePathを見ると微妙に違っていてどうやらバージョン違いのようです。

<dict><!-- ... --><key>DictionaryPackageDisplayName</key><string>ウィズダム英和辞典 / ウィズダム和英辞典</string><!-- ... --><key>__BaseURL</key><string>http://appldnld.apple.com/osxassets/058-55450-2016008023-8A4D782E-661F-11E6-B483-D75733D2D062/</string><!-- ... --><key>__RelativePath</key><string>com_apple_MobileAsset_DictionaryServices_dictionaryOSX/1a184f41ee27bbb203ba596a25c8ea104c13d98f.zip</string></dict><dict><!-- ... --><key>DictionaryPackageDisplayName</key><string>ウィズダム英和辞典 / ウィズダム和英辞典</string><!-- ... --><key>__BaseURL</key><string>http://appldnld.apple.com/ios11.0/048-04169-20171219-3E638938-E064-11E7-BED0-328EE988D09B/</string><!-- ... --><key>__RelativePath</key><string>com_apple_MobileAsset_DictionaryServices_dictionaryOSX/69d9a5c927f04857f6abf7a0a9f144e194b5d0ce.zip</string></dict><dict><!-- ... --><key>DictionaryPackageDisplayName</key><string>ウィズダム英和辞典 / ウィズダム和英辞典</string><!-- ... --><key>__BaseURL</key><string>http://appldnld.apple.com/ios11.0/091-65654-20180306-F43DDB4C-1F01-11E8-B071-BA9988D28C9D/</string><!-- ... --><key>__RelativePath</key><string>com_apple_MobileAsset_DictionaryServices_dictionaryOSX/3fb6544c58d7f04015ded27812a932cbbb3122ad.zip</string></dict><dict><!-- ... --><key>DictionaryPackageDisplayName</key><string>ウィズダム英和辞典 / ウィズダム和英辞典</string><!-- ... --><key>__BaseURL</key><string>http://updates-http.cdn-apple.com/2018/ios/091-97251-20180828-DB88F352-A4B7-11E8-A246-39142147FE1B/</string><!-- ... --><key>__RelativePath</key><string>com_apple_MobileAsset_DictionaryServices_dictionaryOSX/c15aa908a684112f63017e47bb6b289e99977135.zip</string></dict><dict><!-- ... --><key>DictionaryPackageDisplayName</key><string>ウィズダム英和辞典 / ウィズダム和英辞典</string><!-- ... --><key>__BaseURL</key><string>http://updates-http.cdn-apple.com/2019/ios/041-71820-20190919-DB836C8E-DA9E-11E9-A9E0-C30E3E8547A6/</string><!-- ... --><key>__RelativePath</key><string>com_apple_MobileAsset_DictionaryServices_dictionaryOSX/7725cba5b321a8308a603a40ac808699175c4aae.zip</string></dict>

どのURLか分からないので、 /System/Library/AssetsV2/com_apple_MobileAsset_DictionaryServices_dictionaryOSXフォルダ内にある、正しく動作している他の辞書データの識別子がないか探してみると、 __BaseURLhttp://updates-http.cdn-apple.com/2019/ios/で始まるところの __RelativePathのzipファイル名と一致します。

これでダウンロードすべきファイルは http://updates-http.cdn-apple.com/2019/ios/041-71820-20190919-DB836C8E-DA9E-11E9-A9E0-C30E3E8547A6/com_apple_MobileAsset_DictionaryServices_dictionaryOSX/7725cba5b321a8308a603a40ac808699175c4aae.zipであることがわかりました。

このファイルをダウンロード、展開して、 Assetsフォルダにある Sanseido The WISDOM English-Japanese Japanese-English Dictionary.dictionaryを前述の通り ~/Library/Dictionariesフォルダにコピーしてみましたが、改善しませんでした。

そこで、正しく動作している他の辞書データと同様に /System/Library/AssetsV2/com_apple_MobileAsset_DictionaryServices_dictionaryOSXフォルダに置いてみます。とはいえ、 /SystemフォルダはEl Capitan以降導入されたSystem Integrity Protection(SIP)によって保護されているため通常はアクセスできません。そこで一旦これを無効にします。

やり方はこちら。
Configuring System Integrity Protection

To enable or disable System Integrity Protection, you must boot to Recovery OS and run the csrutil(1) command from the Terminal.

Boot to Recovery OS by restarting your machine and holding down the Command and R keys at startup.
Launch Terminal from the Utilities menu.
Enter the following command:
$ csrutil enable

まとめると、⌘+Rを押しながらリカバリモードで起動したあと、「ユーティリティ」メニューからターミナルを起動、 csrutil disableコマンドを実行して再起動します。

こうして、先ほどダウンロード、展開した 7725cba5b321a8308a603a40ac808699175c4aaeフォルダを 7725cba5b321a8308a603a40ac808699175c4aae.assetsにリネームして /System/Library/AssetsV2/com_apple_MobileAsset_DictionaryServices_dictionaryOSXフォルダにコピーし、念のため chmodchown -Rでパーミッションと所有者を正しく動作している他の辞書データと合わせておきます。
また、リカバリモードで再起動して csrutil enableを実行、再起動して無効にしたSIPを有効にしておきます。

そして、辞書.appを起動してみると…。
スクリーンショット 2019-11-27 午後3.00.48.png
Hooray!🎉🎉🎉

こちらからは以上です。

Pythonで辞書のメモリサイズを確認する方法

こんにちは、株式会社LIFULLの二宮です。去年のAdvent Calendarの埋め残しが心残りなので、小ネタですが投稿しておきます。

とある実装中に、変数のメモリの使用量を大まかに見積もる必要がありました。こちらの記事でも紹介されている通り、 sys.getsizeofで変数のメモリ上のサイズを表示できるのですが、

importsysprint(sys.getsizeof({"key":"a"}))# => 248
# 文字サイズが明らかに大きいのに同じ値が出てしまう
print(sys.getsizeof({"key":"a"*10000}))# => 248

というのも、Pythonの辞書の実体は「ハッシュテーブルにオブジェクトへの参照を保存したもの」で、中の文字列のメモリ上のサイズは考慮されません。このあたりの話は、こちらの記事が参考になると思います。

(日本語でいい記事がRubyのものしか見つけられませんでした。ただこのあたりの話はRubyもPythonも共通しています。)

中のオブジェクトのサイズまで考慮するには、公式ドキュメントのこちらのリンク先の記事を参考に実装する必要があります。

getsizeof()を再帰的に使い、コンテナとその中身のサイズを割り出す例は、 再帰的な sizeofのレシピを参照してください。

ただ、私の場合は文字列のみが入った辞書(jsonにシリアライズ可能)で、概算値を知りたかっただけなので、こちらのStackOverflowの書き込みに従って、 json.dumpsの結果で済ませてしまいました。

>>>first='abc'*1000>>>second='def'*1000>>>my_dictionary={'first':first,'second':second}>>>getsizeof(first)3049>>>getsizeof(second)3049>>>getsizeof(my_dictionary)288>>>getsizeof(json.dumps(my_dictionary))6076>>>size=getsizeof(my_dictionary)>>>size+=sum(map(getsizeof,my_dictionary.values()))+sum(map(getsizeof,my_dictionary.keys()))>>>size6495

参考記事

あるシリーズの文字列が辞書のKeyにあった時、辞書のValueに文字列を変換させる

やりたいこと : あるSeriesが辞書のkeyにあった時、辞書のvalueに値を変換させる

【元のデータ】
hoge = pd.Series(["3回目", "1回目", "5回目", "5回目", "2回目", "4回目"])

【変換リスト】
name_lis = {"1回目": "01.first",
"2回目": "02.second",
"3回目": "03.third",
"4回目": "04.fourth",
"5回目": "05.fifth"
}

mapでSeriesを囲う(@nkayさんありがとうございます!)

hoge.map(name_lis)

辞書でSeriesを囲う

hoge.apply(lambda x: name_lis[x])

0 03.third
1 01.first
2 05.fifth
3 05.fifth
4 02.second
5 04.fourth
dtype: object

辞書型データのvalueの直積を返したい

やりたいこと

pythonで、辞書が持つvalue同士の直積を、対応するkeyを変えずに辞書としてそれぞれ出力したい。

具体的には

{'A':[1,2,3],'B':[4,5]}

という辞書が存在するときに

[{'A':1,'B':4},{'A':1,'B':5},{'A':2,'B':4},{'A':2,'B':5},{'A':3,'B':4},{'A':3,'B':5}]

といった具合のリストが得られるようにしたい。

前提知識

itertools.product()で直積を求められます。

https://docs.python.org/ja/3/library/itertools.html#itertools.product

実装

test1.py
importitertoolstest1={'A':[1,2,3],'B':[4,5]}product=[xforxinitertools.product(*test1.values())]result=[dict(zip(test1.keys(),r))forrinproduct]forrinresult:print(r)
console
{'A': 1, 'B': 4}
{'A': 1, 'B': 5}
{'A': 2, 'B': 4}
{'A': 2, 'B': 5}
{'A': 3, 'B': 4}
{'A': 3, 'B': 5}

valueの型が異なっていてもOK

test2.py
importitertoolstest2={'A':['TEST',1,2.5],'B':[[3,4],5]}product=[xforxinitertools.product(*test2.values())]result=[dict(zip(test1.keys(),r))forrinproduct]forrinresult:print(r)
console
{'A': 'TEST', 'B': [3, 4]}
{'A': 'TEST', 'B': 5}
{'A': 1, 'B': [3, 4]}
{'A': 1, 'B': 5}
{'A': 2.5, 'B': [3, 4]}
{'A': 2.5, 'B': 5}

注意点

直積を求めたい辞書のvalueはlist型でなければなりません。
例えば以下の様な辞書を変換しようとするとエラーとなります。

test3.py
importitertoolstest3={'A':1,'B':[2,3],'C':[4,5,6]}product=[xforxinitertools.product(*test3.values())]result=[dict(zip(test1.keys(),r))forrinproduct]forrinresult:print(r)
console
Traceback (most recent call last):
  File "test3.py", line 5, in <module>    product = [x for x in itertools.product(*test1.values())]
TypeError: 'int' object is not iterable

この様な場合は一旦valueを舐めてlistでなければlistに変換してあげれば良いです。

test4.py
importitertoolstest4={'A':1,'B':[2,3],'C':[4,5,6]}test4_after=dict([(key,valiftype(val)islistelse[val])forkey,valintest4.items()])product=[xforxinitertools.product(*test4_after.values())]result=[dict(zip(test4_after.keys(),r))forrinproduct]forrinresult:print(r)
console
{'A': 1, 'B': 2, 'C': 4}
{'A': 1, 'B': 2, 'C': 5}
{'A': 1, 'B': 2, 'C': 6}
{'A': 1, 'B': 3, 'C': 4}
{'A': 1, 'B': 3, 'C': 5}
{'A': 1, 'B': 3, 'C': 6}

key値を捨てる場合

key値を捨ててvalueの直積だけ欲しい場合はzipの部分を消せばOK。

test5.py
importitertoolstest5={'A':[1,2],'B':[4,5,6]}product=[xforxinitertools.product(*test5.values())]forrinproduct:print(r)
console
(1, 4)
(1, 5)
(1, 6)
(2, 4)
(2, 5)
(2, 6)

【Python】辞書型変数(dictionary)

辞書型変数とは

辞書型変数は、複数の値をもつリストのような変数である。
リストではindexによってリスト内の要素を参照することができる。
一方で、辞書型変数ではkeyによってvalue(値)を参照することができる。
以下の例でリストと辞書型変数の違いを確認して頂きたい。

dict.py
list={"apple","banana","chocolate"}print(list[0])>>>appledict={"a":"apple","b":"banana","c":"chocolate"}print(dict["a"])>>>apple

上例で、変数名=dictが辞書型変数である。
a→apple
b→banana
c→chocolateに対応する。
a , b , c のことをkeyといい、
apple , banana , chocolate のことをvalueという。

辞書型変数の宣言の仕方(dict関数,zip関数)

辞書型変数はdict関数、zip関数を使っても宣言できる。
書きやすいと感じる方法で宣言して頂ければと思う。

★dict関数
変数名=dict(key1=value1 , key2=value2 , ・・・)
で宣言することが出来る。

dict_f.py
dict1=dict(a="apple",b="banana",c="chocolate")print(dict1["a"])>>>apple

これとzip関数を併用することでkeyとvalueを明確にした記述で宣言することが出来る。

dict_zip_f.py
key=["a","b","c"]value=["apple","banana","chocolate"]dict1=dict(zip(key,value))print(dict1["a"])>>>apple

key , value の取り出し方

辞書型変数に含まれるkeyを参照する方法は.keys()メソッド、
valueを参照する方法は.values()メソッドを使う。

dict_zip_f.py
key=["a","b","c"]value=["apple","banana","chocolate"]dict1=dict(zip(key,value))print(dict1.keys())>>>dict_keys(['a','b','c'])print(dict1.values())>>>dict_values(['apple','banana','chocolate'])

for文を使って一つずつ要素を取り出すこともできる。

dict_zip_f.py
key=["a","b","c"]value=["apple","banana","chocolate"]dict1=dict(zip(key,value))formindict1.keys():print(m)>>>a>>>b>>>c

要素の追加

辞書型変数の宣言後に、新たに要素を追加したい場合は
変数名[追加したいkey]=追加したいkeyに対するvalue
で追加することが出来る。

dict_zip_f.py
key=["a","b","c"]value=["apple","banana","chocolate"]dict1=dict(zip(key,value))print(dict1)>>>{'a':'apple','b':'banana','c':'chocolate'}dict1["d"]="dessert"#ここで新しいkeyとvalueを追加
print(dict1)>>>{'a':'apple','b':'banana','c':'chocolate','d':'dessert'}

辞書型変数に別の辞書型変数を組み合わせたいときは、
組み合わせ先dict.update(取り込みたいdict)
で二つの辞書から一つの辞書を生成することが出来る。

dict_zip_f.py
key1=["a","b","c"]value1=["apple","banana","chocolate"]dict1=dict(zip(key1,value1))key2=["y","n"]value2=["yes","no"]dict2=dict(zip(key2,value2))dict1.update(dict2)print(dict1)>>>{'a':'apple','b':'banana','c':'chocolate','y':'yes','n','no'}

要素の削除

要素の削除はdel文、clearメソッドの二つがある。
★del文
いくつかの要素を削除することが出来る。
del 変数名[削除したいkey1] , 変数名[削除したいkey2] , ...

dict_zip_f.py
key1=["a","b","c"]value1=["apple","banana","chocolate"]dict1=dict(zip(key1,value1))deldict1["a"],dict1["c"]print(dict1)>>>{'b':'banana'}

★clearメソッド
全ての要素を削除する。
変数名.clear()

dict_zip_f.py
key1=["a","b","c"]value1=["apple","banana","chocolate"]dict1=dict(zip(key1,value1))dict1.clear()print(dict1)>>>{}

Mac版ExcelでVBAのDictionaryを使えるようにするまで

Mac版のExcelを使ってVBAを開発していると使えない機能があって嫌になることが投げ出したくなることが、VBAの勉強を初めて1週間で、すでに2回もありました。(いやもっとあったけど、Macに限る話じゃないからカウントしません。)

1つ目は、ユーザーフォームを開発できない
これ致命的じゃないでしょうか?マイクロソフトさん。2011年のMac版Excelでは使えていたみたいですけど、2016年版くらいから使えなくなったという、後のバージョンで使えなくなるというマイクロソフトさんお得意の、機能を減らして新バージョンを購入させる戦法ですね。。

2つ目は、Mac版ではDictionaryが使えないということ。これも致命的というか、もうVBAはMacで開発できない、動かないと言っているようなもの。ユーザーフォームが使えないのは妥協しても流石に根幹的な機能であるDictionaryが使えないのは、許せなかったので(というかそれくらいで勉強をやめてしまっては勿体無いので)少し調べてみたら、案外簡単にできました。簡単だったと言っても少し手こずったのでやったことまとめてみました。

1、StackoverflowでDictionary.clsとKeyValuePair.clsをダウンロードする
2、以上の2つのファイルをプロジェクトのimportする
3、使う。ただし、For EachでValueを取り出すときは一手間必要

まず1はStackoverflowのこのページへ飛んでVBA (Excel) Dictionary on Mac?
sysmodさんの回答からZipファイルをもらってください。(または直接貼っておきますダウンロード

そして2はプロジェクトを選択して、2つの.clsをimport
(この際、.clsファイルの中のattribute等は一切変更しなくて大丈夫です。)

最後に3ですが、あとはクラスからインスタンス作って、ドット以降自動で候補を上げてくれるようになるんですが、For Each でValueを取り出す時にコンパイルエラー Compile error: ByRef argument type mismatchが出てしまうので一作業が必要。

まず直接変数をItem()の引数にすると上記のエラーが出るので、一度別の変数に格納してから、その変数をItem()の引数として渡せばうまく動くようになります。

For Each vKey In vKeys
Dim k As String: k = vKey
Debug.Print k & " = " & dict.Item(k)
Next

以上、Mac版ExcelでVBAのDictionaryを使えるようにするまででした。
将来的にはユーザーフォームなども開発してみたいので、いずれにしてもWindow環境を検討していますが、しばらくの間はMacで開発できるところまで頑張ってみたいと思っています。

【Python】 PythonでJSONを使う

はじめに

Pythonでjsonのデータを扱うことがあったのですが、Python標準のdict型と親和性があるので紹介します。

jsonとは

こちらとても参考になります。ありがとうございます。
JavaScript Object Notation
データを表現するための記法です。
JavaScriptの構文に似ていますが、JavaScriptとは独立して扱われます。
JavaScript以外のプログラミング言語でもJSONを扱うことができる機能が準備されています。

この記事はJSONを使っていくことを中心な記事なので細かい説明は省略します。
形式は以下のようなものです。keyに対してvalueが紐づいているという感じです。

{
  "key":value,
  "key":value,
  "key":value
}

keyとvalueのセットというのはPythonのdictと同じですね。

Pythonのjsonモジュール

Pythonにjsonモジュールが存在します。dict⇔jsonの変換を主にやってくれます。

dictからjsonへの変換にはjson.dumpsを使います。
dictの型はもちろんdictですが、変換後はstr型になります。

importjsonsample_dict={"Japan":"Tokyo","UK":"London","USA":"Washington, D.C."}print(type(sample_dict))print(sample_dict)# 出力
# <class 'dict'>
# {'Japan': 'Tokyo', 'UK': 'London', 'USA': 'Washington, D.C.'}
# 変換
sample_json=json.dumps(sample_dict)print(type(sample_json))print(sample_json)# 出力
# <class 'str'>
# {"Japan": "Tokyo", "UK": "London", "USA": "Washington, D.C."}

pandasからjsonに変換する

pandas DataFrameやpandas Seriesからもjsonに変換することができます。個人的にpandasはよく使うのでありがたいです。
pandasDataFrame(Series)からjsonへの変換はto_json()を使います。

importpandasaspddf_sample=pd.DataFrame([[1,1,1],[2,3,4],[8,7,6]],columns=["x","y","z"],index=["A","B","C"])json_df=df_sample.to_json()print(df_sample)print(json_df)# 出力
xyzA111B234C876{"x":{"A":1,"B":2,"C":8},"y":{"A":1,"B":3,"C":7},"z":{"A":1,"B":4,"C":6}}

jsonを読み込む

jsonのデータを読み込むには json.loads を使います。
先ほどのsample_jsonを使います。

dict_json=json.loads(sample_json)print(type(dict_json))print(dict_json)# 出力
# <class 'dict'>
# {'Japan': 'Tokyo', 'UK': 'London', 'USA': 'Washington, D.C.'}

もちろんdict_jsonはPythonのdictなので

print(dict_json['Japan'])# 出力
# Tokyo

このような形でアクセスできます。

おわりに

jsonはpythonだけでなくほかの言語でも使えるので言語間の通信に便利です。ここでは最低限しか説明してませんがぜひ使ってみてください!

思ったこと

自分が使ってみて、とかの視点で書いているのでまったく網羅的に触れることができていない記事ばかりだなと思いました。とりあえず今後も自分が使ってみたものについて書いてみて、見直す必要があった場合は記事を更新していこうかなと思います。

【Python】辞書の値に関数を入れたときの実行時間

switch (select) 構文のないPythonではその代わりに辞書を使うことがありますが,
その実行時間に関する注意です.

結論

  • 関数自体を入れる:{ key: func }
    • 短時間で済む
    • 実行されるのはキーによって選択された関数のみ
  • 関数の返り値を入れる:{ key: func() }
    • 余計な時間がかかる
    • 全ての関数が一旦実行され,その返り値が入る

関数の引数が異なる場合は,

  • 予め関数の引数に( *args, **kwargs )を用意しておきその差を吸収する
  • 与える引数自体も辞書にしてしまう
    • e.g. dict_kwargs = { 1: { 'arg1': arg1 }, 2: { 'arg2': arg2 } }

という方法が考えられます.

以下の2つの関数を場合分けして実行する場合を考えます.
(実行時間が知りたいので遅くなるような書き方をしています.)

defmyfunc1():nx,ny=50,40arr=np.empty((nx,ny))foriyinrange(ny):forixinrange(nx):arr[ix,iy]=ix*ix+iy*iyreturnarrdefmyfunc2():nx,ny=50,40arr=np.empty((nx,ny))foriyinrange(ny):forixinrange(nx):arr[ix,iy]=1returnarr

その上で,以下の3通りの実行方法にかかる時間を計測しました(JupyterLabの%%timeit使用,下はmyfunc1のみ).

# 直接関数を実行
myfunc1()# { key: func } 型の辞書から実行
key=1dict_func={1:myfunc1,2:myfunc2}dict_func[key]()# { key: func() } 型の辞書から実行
dict_return={1:myfunc1(),2:myfunc2()}dict_return[key]

結果は以下の通りでした.{ key: func() } 型では,両方の関数が実行される時間に近い時間がかかっていることが分かります.

# 直接実行
1:488µs±26.7µsperloop(mean±std.dev.of7runs,1000loopseach)2:286µs±925nsperloop(mean±std.dev.of7runs,1000loopseach)# { key: func } 型
1:461µs±332nsperloop(mean±std.dev.of7runs,1000loopseach)2:298µs±22.1µsperloop(mean±std.dev.of7runs,1000loopseach)# { key: func() } 型
1:763µs±3.96µsperloop(mean±std.dev.of7runs,1000loopseach)2:771µs±31.6µsperloop(mean±std.dev.of7runs,1000loopseach)

Perlにおけるhash(ハッシュ)は、Pythonにおける辞書(ディクショナリ)

Perlにおけるhashは、Pythonにおける辞書

・Key(キー)とvalue(値)をセットで格納する。

正直、私はPerlのhashの機能はよく知らない。

Pythonで辞書は
x = {"book":"Yomitai", "hon":3}
のように、key:valueを{}の中で定義する。

kiri.py
kiri={"price":20,"number":12}forkeyinkiri:print(key+":"+str(kiri[key]))

for key, value in kiri.items():
のように、itemsメソッドを使うと、辞書のキーと値をセットで取得できる。
その時は、str(kiri[key])では無くて、str(value)で良い。

更に、formatを使うと簡潔にできる。

formatkiri.py
kiri={"price":20,"number":12}forkey,valueinkiri.items():print("{0}:{1}".format(key,value))

(書き足す予定)

【JavaScript】連想配列の値(value)で並び替え

連想配列の値で並び替えをしてみた。

以下のような連想配列を値が大きい順にソートしたかったので、挑戦してみました。

letobj={"Apple":10,"Orange":5,"Banana":12,"Mango":2,"Melon":7,}

キーと値にそれぞれキーをつけた連想配列を格納した配列の作成

letarr=letarr=Object.keys(obj).map((e)=>({key:e,value:obj[e]}));console.log(arr);// [ { key: 'Apple', value: 10 },//   { key: 'Orange', value: 5 },//   { key: 'Banana', value: 12 },//   { key: 'Mango', value: 2 },//   { key: 'Melon', value: 7 } ]

※mapに訂正しました。

キー[value]でソート

上記で作成した連想配列を格納した配列をキー[value]でソートします。

arr.sort(function(a,b){if(a.value<b.value)return1;if(a.value>b.value)return-1;return0;});console.log(arr);// [ { key: 'Banana', value: 12 },//   { key: 'Apple', value: 10 },//   { key: 'Melon', value: 7 },//   { key: 'Orange', value: 5 },//   { key: 'Mango', value: 2 } ]

いぇーい(^^)
すごく回りくどいけどソートすることができました!

Browsing Latest Articles All 31 Live