{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<p style=\"text-align:center\">\n",
    "    <a href=\"https://nbviewer.jupyter.org/github/twMr7/Python-Machine-Learning/blob/master/07-Dict_Operations.ipynb\">\n",
    "        Open In Jupyter nbviewer\n",
    "        <img style=\"float: center;\" src=\"https://nbviewer.jupyter.org/static/img/nav_logo.svg\" width=\"120\" />\n",
    "    </a>\n",
    "</p>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/twMr7/Python-Machine-Learning/blob/master/07-Dict_Operations.ipynb)\n",
    "\n",
    "# 7. `dict` 容器操作\n",
    "\n",
    "`dict` 是非序列式的資料結構,元素內容的儲存是成對的 key 和對應的 value。語法使用冒號組成 key 和 value 對應組 `key:value`,以逗號 `,` 分隔對應組元素,用大括號(curly braces)`{` `}` 成對包住所有元素。 同一個 `dict` 中的 `value` 可以存放異質類型資料。 `dict` 也可以是巢狀的,也就是 `value` 中含有另外一個 `dict` 類型的資料。\n",
    "\n",
    "元素內容必須用 `key` 來存取,語法為 **`[ key ]`**。\n",
    "\n",
    "| Dict 範例                          | 說明                                          |\n",
    "|------------------------------------|-----------------------------------------------|\n",
    "| `{}`                               | 空的 dict                                     |\n",
    "| `{'alpha': 2, 'beta': 3}`          | 兩個元素的 dict                               |\n",
    "| `{ 'parameter': { 42: 1.5, 'angle': 30 }, 'date': ['2018-06-08'] }` |  巢狀、異質的 dict |\n",
    "| `dict([['a', 1], ['b', 2], ['c', 3]])` | 由list中建構一個新的dict物件              |\n",
    "\n",
    "- 內建函式 `dict()` 可以用來從現有物件的資料實體中生成一個新的tuple。\n",
    "- 內建函式 `len()` 可以用來回傳容器裡key-value對的個數。\n",
    "\n",
    "`dict` 在建立後,元素的 `value` **可以**就地變更(mutable),`key` 不可以就地變更(immutable),也不允許 mutable 的物件類型當 `key`。 `dict` 提供的方法,請參閱官方文件 [4.10 Mapping Types — dict](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict):\n",
    "- `clear()` 清除所有容器內的內容。\n",
    "- `copy()` 複製。\n",
    "- `get()` 回傳某個 key 的對應 value,沒有的話就回傳預設值。\n",
    "- `pop()` 回傳並移除某個 key 的對應 value,沒有的話就回傳預設值。\n",
    "- `items()` 回傳可迭代的所有 (key, value) view。\n",
    "- `keys()` 回傳可迭代的所有 key 的 view。\n",
    "- `values()` 回傳可迭代的所有 value 的 view。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### § `dict` 成員的基本操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "extno = {'Adam': 3030, 'Brown': 2543}\n"
     ]
    }
   ],
   "source": [
    "extno = { 'Adam': 3030, 'Brown': 2543}\n",
    "print('extno = {}'.format(extno))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "ename": "KeyError",
     "evalue": "'Cathy'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mKeyError\u001b[0m                                  Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-2-70ab3a4166b7>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;31m# 讀取一個不存在的 key 會出現 KeyError 的錯誤\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mextno\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'Cathy'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mKeyError\u001b[0m: 'Cathy'"
     ]
    }
   ],
   "source": [
    "# 讀取一個不存在的 key 會出現 KeyError 的錯誤\n",
    "print(extno['Cathy'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "extno = {'Adam': 3030, 'Brown': 2543, 'Cathy': 1234}, 3個成員\n"
     ]
    }
   ],
   "source": [
    "# 指定一個沒有的 key 就會新增\n",
    "extno['Cathy'] = 1234\n",
    "print('extno = {}, {}個成員'.format(extno, len(extno)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Brown's extension number = 2567\n"
     ]
    }
   ],
   "source": [
    "# 修改對應值\n",
    "extno['Brown'] = 2567\n",
    "print(\"Brown's extension number = {}\".format(extno['Brown']))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "after delete Adam, extno = {'Brown': 2567, 'Cathy': 1234}, 2個成員\n"
     ]
    }
   ],
   "source": [
    "# 刪除\n",
    "del extno['Adam']\n",
    "print('\\nafter delete Adam, extno = {}, {}個成員'.format(extno, len(extno)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Is Adam in the ext record? (False)\n"
     ]
    }
   ],
   "source": [
    "# 檢查成員是否存在某個 key\n",
    "print('Is Adam in the ext record? ({})'.format('Adam' in extno))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Add Adam back, Adam's extension number is now = 3080\n",
      "extno = {'Brown': 2567, 'Cathy': 1234, 'Adam': 3080}\n"
     ]
    }
   ],
   "source": [
    "# 把 Adam 加回去\n",
    "extno['Adam'] = 3080\n",
    "print(\"Add Adam back, Adam's extension number is now = {}\".format(extno['Adam']))\n",
    "print('extno = {}'.format(extno))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### § `dict` 成員的 Views"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dict_items([('Brown', 2567), ('Cathy', 1234), ('Adam', 3080)])\n"
     ]
    }
   ],
   "source": [
    "# items() 的方法回傳一個 dict_items 的 view 物件,包含所有成員的成對的 key-value\n",
    "print(extno.items())\n",
    "# 把 view 卸載\n",
    "item_list = list(extno.items())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dict_keys(['Brown', 'Cathy', 'Adam'])\n"
     ]
    }
   ],
   "source": [
    "# keys() 的方法回傳一個 dict_keys 的 view 物件,包含所有成員的 key\n",
    "print(extno.keys())\n",
    "# 把 view 卸載\n",
    "key_list = list(extno.keys())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dict_values([2567, 1234, 3080])\n"
     ]
    }
   ],
   "source": [
    "# values() 的方法回傳一個 dict_values 的 view 物件,包含所有成員的 value\n",
    "print(extno.values())\n",
    "# 把 view 卸載\n",
    "value_list = list(extno.values())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### § 多種生成新的 `dict` 物件的方法\n",
    "- `dict()`\n",
    "- `dict.fromkeys()`\n",
    "- `dict(zip())`\n",
    "\n",
    "Python 的內建函式 `zip()` 可以用來將多組序列物件裡的成員,按照對應順序分拆打包在一序列的 tuple 裡。`dict()` 會將每個 tuple 的第一個元素當成 key。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dict from key-value sequence = {'Brown': 2567, 'Cathy': 1234, 'Adam': 3080}\n"
     ]
    }
   ],
   "source": [
    "# 可以從原本就是 key-value pair 的序列終生成\n",
    "dict_from_view = dict(item_list)\n",
    "print('dict from key-value sequence = {}'.format(dict_from_view))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dict from key list = {'Brown': 10000, 'Cathy': 10000, 'Adam': 10000}\n"
     ]
    }
   ],
   "source": [
    "# fromkeys() 可以從 list 中生成新的字典容器,value 都用預設值\n",
    "dict_from_keys = dict.fromkeys(key_list, 10000)\n",
    "print('dict from key list = {}'.format(dict_from_keys))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "two lists after zipped: [(2567, 'Brown'), (1234, 'Cathy'), (3080, 'Adam')]\n"
     ]
    }
   ],
   "source": [
    "# zip 把兩個 list 裡的成員分拆打包成 tuple 序列\n",
    "print('\\ntwo lists after zipped: {}'.format(list(zip(value_list, key_list))))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dict from zipping 2 lists = {2567: 'Brown', 1234: 'Cathy', 3080: 'Adam'}\n"
     ]
    }
   ],
   "source": [
    "# 新的 dict 用原本的 value 來當 key\n",
    "dict_from_zip = dict(zip(value_list, key_list))\n",
    "print('dict from zipping 2 lists = {}'.format(dict_from_zip))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### § 常用的 `dict` 物件方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dict_from_view 有 3 個成員\n"
     ]
    }
   ],
   "source": [
    "# 成員個數\n",
    "print('dict_from_view 有 {} 個成員'.format(len(dict_from_view)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\"Operator\" 不是 dict_from_view 的成員? (True)\n",
      "dict_from_view[\"Operator\"] = 9999\n"
     ]
    }
   ],
   "source": [
    "# get() 的方法常見用於讀取設定檔,沒有設定的參數就回傳預設值,不會沒有這個 key 就發生錯誤\n",
    "print('\"Operator\" 不是 dict_from_view 的成員? ({})'.format('Operator' not in dict_from_view))\n",
    "print('dict_from_view[\"Operator\"] = {}'.format(dict_from_view.get('Operator', 9999)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "dict_from_view 的成員和 extno 都一樣嗎? (True),\n",
      "dict_from_view 是不是跟 extno 參考同一個物件? (False)\n"
     ]
    }
   ],
   "source": [
    "# 檢查從 view 裡產生的物件\n",
    "print('\\ndict_from_view 的成員和 extno 都一樣嗎? ({}),\\ndict_from_view 是不是跟 extno 參考同一個物件? ({})'\n",
    "      .format(extno == dict_from_view, extno is dict_from_view))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "dict_from_copy 的成員和 extno 都一樣嗎? (True),\n",
      "dict_from_copy 是不是跟 extno 參考同一個物件? (False)\n"
     ]
    }
   ],
   "source": [
    "# 也可以用 copy() 複製一份新的 dict 物件\n",
    "dict_from_copy = extno.copy()\n",
    "print('\\ndict_from_copy 的成員和 extno 都一樣嗎? ({}),\\ndict_from_copy 是不是跟 extno 參考同一個物件? ({})'\n",
    "      .format(extno == dict_from_copy, extno is dict_from_copy))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7.1 Dict Comprehension\n",
    "\n",
    "對於可拆解出成對的 (key, value) 的序列容器或可迭代物件 S 進行操作,並生成一個新的 `dict` 物件。\n",
    "\n",
    "| 成員的操作                                 | 說明                                                          |\n",
    "|--------------------------------------------|---------------------------------------------------------------|\n",
    "| `{key:value for (key,value) in S}`         | 針對每個 S 的成員 x 做運算,運算結果生成新的 dict 物件        |\n",
    "| `{key:value for (key,value) in S if 條件}` | 針對每個***符合條件***的成員 x 做運算,運算結果生成新的 dict 物件 |\n",
    "\n",
    "Dict comprehension 語法結構也可組成相當豐富的條件式迭代運算,但需注意`dict`容器的 *key* 不能重複,若重複指定不同 value 給同一個 key,結果對應的 value 會是最後指定的值。\n",
    "```\n",
    "{key:value運算表示句 for x1 in S1 if 條件1\n",
    "                     for x2 in S2 if 條件2 ...\n",
    "                     for xN in SN if 條件N}\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0: '0', 1: '1', 2: '2', 3: '3', 4: '4'}\n",
      "{'a': 97, 'b': 98, 'c': 99}\n"
     ]
    }
   ],
   "source": [
    "# 可迭代物件生成 dict 物件\n",
    "print({x: str(x) for x in range(5)})\n",
    "print({x: ord(x) for x in ['a', 'b', 'c']})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0: 0, 2: 4, 4: 16, 6: 36, 8: 64}\n"
     ]
    }
   ],
   "source": [
    "# 條件式挑選部份成員作處理\n",
    "print({x: x ** 2 for x in range(10) if x % 2 == 0})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{1: 6, 2: 6, 3: 6}\n"
     ]
    }
   ],
   "source": [
    "# 在 dict comprehension 使用巢狀迴圈,注意結果可能不如原先預期\n",
    "print({k: v for k in range(1, 4) for v in range(4, 7)})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0: 'd', 1: 'i', 2: 'c', 3: 't', 4: ' ', 5: 'c', 6: 'o', 7: 'm', 8: 'p', 9: 'r', 10: 'e', 11: 'h', 12: 'e', 13: 'n', 14: 's', 15: 'i', 16: 'o', 17: 'n'}\n"
     ]
    }
   ],
   "source": [
    "# 使用 enumerate 把迭代次序當成 key\n",
    "print({k: v for (k, v) in enumerate('dict comprehension')})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'a': 1, 'b': 2, 'c': 3}\n"
     ]
    }
   ],
   "source": [
    "# 可以使用 zip 兩兩配對\n",
    "print({k: v for (k, v) in zip(['a', 'b', 'c'], [1, 2, 3])})"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}