import heapq
nums = [1,8,23,44,56,12,-2,45,23]
print(heapq.nlargest(3,nums))
print(heapq.nsmallest(3,nums))

portfolio = [
    {'name':'IBM','shares':100,'price':91.1},
    {'name':'AAPL','shares':50,'price':543.22},
    {'name': 'FB', 'shares': 200, 'price': 21.09},
    {'name': 'HPQ', 'shares': 35, 'price': 31.75},
    {'name': 'YHOO', 'shares': 45, 'price': 16.35},
    {'name': 'ACME', 'shares': 75, 'price': 115.65}
]
cheap = heapq.nsmallest(4,portfolio,key = lambda s : s['price'])
expensive = heapq.nlargest(3,portfolio,key = lambda s:s['price'])

print('The fours of cheapest:%s\nThe threes of expensivest:%s'%(cheap,expensive))

heapq.heapify(nums)
nums

heapq.heappop(nums) # heap -- 堆

"""
Explanation: 1.4 查找最大或最小的N个元素
怎样从一个集合中获得最大或者最小的N个元素列表？
End of explanation
"""


class PriorityQueue:
    def __init__(self):
        self._queue = []
        self._index = 0
    
    def push(self,item,priority):
        heapq.heappush(self._queue,(priority,self._index,item))
        self._index += 1
    '''
        push 按照queue 优先级priority 是以从低到高起 若 -priority is 以从高到低起
    '''
    
    def pop(self):
        return heapq.heappop(self._queue)[-1]

class Item:
    def __init__(self,name):
        self.name = name
    
    def __repr__(self):
        return 'Item({!r})'.format(self.name)

q = PriorityQueue()
q.push(Item('foo'),1)
q.push(Item('bar'),5)
q.push(Item('spqm'),4)
q.push(Item('grok'),1)

q

q.pop() # pop 1

q.pop() # pop 2

q.pop() # pop 3

q.pop() # pop 4

"""
Explanation: 当查找的元素个数较小时(N &lt; nSum)，函数nlargest and nsmalest 是很适合<br>若 仅仅想查找唯一的 最小或最大N=1的元素的话，使用max and min 更快<br>若N的大小的和集合大小接近时，通常先排序在切片更快 sorted(items)[:N] and sorted(items)[-N:]
1.5 实现一个优先级队列
怎样实现一个按优先级排序的队列？ 并且在这个队列上面每次pop操作总是返回优先级最高的那个元素
End of explanation
"""


d = {
    'a':[1,2,3],
    'b':[4,5]
}
e = {
    'a':{1,2,3},
    'b':{4,5}
}

"""
Explanation: pop 1 返回优先级最高的元素<br>针对pop 3 and pop 4 按照其被插入至queue 顺序返回
module heapq ---heapq.heappush() and heapq.pop() 分别在_queue队列中插入和删除第一个元素 同时保证_queue第一个元素拥有最小优先级<br>heapq()函数总是返回"最小的(priority)"的元素--This is Key of 保证queue pop操作返回正确元素的关键 时间复杂度O(log N ) super quick!!!<br>  index -var 保证同等优先级正确排序 如pop3 and pop4
1.6 字典中的键映射多个值
怎样实现一个键对应多个值的字典(也叫 multidict )？
一个dict就是一个键对应一个单值的映射 if you want to 一个键映射多个值 则需要将这多个值放置另外容器 比如 list or set中
End of explanation
"""


from collections import defaultdict

d = defaultdict(list)
d['a'].append(1)
d['a'].append(2)
d['b'].append(3)

d = defaultdict(set)
d['a'].add(1)
d['a'].add(2)
d['b'].add(4)

d

"""
Explanation: 选择list 还是set 取决你的实际要求 if want to keep element 的插入顺序 则选择list ifwant 去掉 repeat element 即使用set<br>you can use collections module 中的defaultdict 来构造这样字典
End of explanation
"""


d = {} # a regular dictionary
d.setdefault('a',[]).append(1)
d.setdefault('a',[]).append(2)
d.setdefault('b',[]).append(4)
# 每次都调用需要创建一个新的初始值的instance(empty list=[])

d

"""
Explanation: 以上d 指的是（创建）新的dict [创建映射实体]<br>if you 只想在一个普通的字典上使用setdefault方法来替代
End of explanation
"""


'''
d = {}
pairs = {'a':[1,2],'b':2,'c':3}
 for key, value in pairs:
    if key not in d:
        d[key] = []
    d[key].append(value)
'''

"""
Explanation: create a 多值映射dict很简单 but if you want to create  yourself 太难啦
End of explanation
"""


'''
d = defaultdict(list)
for key, value in pairs:
    d[key].append(value)
'''

"""
Explanation: But use defaultdict is so easy and simple
End of explanation
"""


from collections import OrderedDict
def ordered_dict():
    d = OrderedDict()
    d['foo'] = 1
    d['bar'] = 2
    d['spa'] = 3
    d['gro'] = 4
    # Outputs 'foo 1','bar 2','spa 3','gro 4'
    ordered_dict()
    for key in d:
        print(key,d[key])

"""
Explanation: 1.7 字典排序
想创建一个dict and 在迭代or序列化这个dict 时可控制element 的顺序
为control 一个dict 中element 的order you can use collections 中的OrderedDict类 在迭代操作时 其会 keep element 元素插入时的order
End of explanation
"""


import json
json.dumps(d)

"""
Explanation: create a 将来序列化 or 编码成其他格式的映射的时候OrderedDict is very useful<br> 精确control JSON编码字段的顺序可使用OrderedDict来构建数据
End of explanation
"""


prices = {
    'AC':45.34,
    'AA':615.2,
    'IAM':205.3,
    'FB':10.765
}
# 对dict进行计算操作 需要利用zip() 将key and value 反转过来
min_price = min(zip(prices.values(),prices.keys()))
print('min_price is %s , %s' % min_price[:])
max_price = max(zip(prices.values(),prices.keys()))
print('max_price is %s , %s' % max_price[:])

prices_sorted = sorted(zip(prices.values(),prices.keys()))

prices_sorted

"""
Explanation: OrderedDict 内部维护这一个根据插入顺序排序的双向链表 每次当一个新的element insert into it and newelement will be 放到 链表的尾部<br>对于一个已经存在键的重复赋值不会改变键的顺序
需要注意的是，一个 OrderedDict 的大小是一个普通字典的两倍，因为它内部维护着另外一个链表。 所以如果你要构建一个需要大量 OrderedDict 实例的数据结构的时候(比如读取100,000行CSV数据到一个 OrderedDict 列表中去)， 那么你就得仔细权衡一下是否使用 OrderedDict 带来的好处要大过额外内存消耗的影响。
1.8 字典的运算
怎样在data dict 中执行一些计算操作
End of explanation
"""


prices_and_names = zip(prices.values(),prices.keys())
print(min(prices_and_names))
print(max(prices_and_names))

"""
Explanation: 需要注意的是 zip function is 创建的一个只能访问一次的迭代器
End of explanation
"""


min(prices)
max(prices)
# 以上是按照key的 字母进行排序并获得最大 or 最小

"""
Explanation: max() arg is an empty sequence

ERROR：表示此时 max 中的参数是一个空的 序列
若是不利用zip() 直接进行普通的数学运算<br>他会作用于key 而不是value
End of explanation
"""


min(prices.values())
max(prices.values())

"""
Explanation: 为弥补以上问题 我就直接提取出 dict中的value
End of explanation
"""


print(min(prices,key=lambda k:prices[k]))
# print(max(prices,value=lambda v:prices[v]))

"""
Explanation: 不过 以上两种方式 都差强人意 我对dict操作 是为了既要显示 key 并要显示 value<br>So 这里要利用到lambda函数
End of explanation
"""


p = {'a':123,'b':123}
print(min(zip(p.values(),p.keys())))
print(max(zip(p.values(),p.keys())))

"""
Explanation: 以上key 函数 可以返回 value 最低的对应key 即value 最低是 10.45 贰最低对应的key是 FB
最先利用的zip 函数就可以"反转"为 （value ，key）元组序列来解决上述问题 当比较此元组时 value会先进性比较 后是key---(这样的话，即可利用简单语句进行实现操作)---
若是出现dict中实体拥有相同的value 在执行 max or min 时会继续判读 key的大小来据此进行判断
End of explanation
"""


