【QuotationTool】主要数据结构

项目链接:https://gitee.com/xyjtysk/quotationTools

采用什么样的数据结构

那么我们希望读出来的数据是这个什么样子呢?也就是数据结构是怎么样的?

既然Excel是个二维表格,那么读出来的数也放在一个二维表格里面得了。

我们一个简化版的例子来看,下面的表格是从原始表格中截出来的一部分。

产品编码 产品型号 数量 标准价(RMB)
0235A0W2 RT-MSR5660 2 50000

这样的缺点在于:取每个元素,需要计算index,不方便编程。

比如我们要取第三行的“RT-MSR5660”,我们需要使用a[1][1]来取,非常不方便。

我们知道最方便取的数据结构为dict,只要传进去一个key,它就会返回一个value,这样的好处是

  • 可以为每一列赋予实际的含义,比如说可以把产品型号的key设为BOM,我们要取“RT-MSR5660”的时候,就可以用a[1]['BOM']

  • 如果要调换列的顺序,可以轻松做到,更为的灵活

    因为每一列赋予了实际的含义,我们根本不用担心具体的顺序

那么具体的数据格式应该是怎么样的呢?

首先我们为所能用到的列分配一个key值,映射表格如下:

NHCT中的列 key值
ID ID
产品编码 BOM
产品型号 typeID
项目名称 description
单套数量 quantity
目录价 unitsNetListPrice
折扣 discount
单价 unitsNetPrice
总价 totalPrice
总目录价 totalListPrice
产线 PL
WATSON_LINE_ITEM_ID waston
备注 remarks

那么下面表格所对应的数据结构可以设计成这个样子。

产品编码 产品型号 数量 标准价(RMB)
0235A0W2 RT-MSR5660 2 50000
[
{"BOM":"产品编码",    
"typeID":"产品型号",    
"description":"项目名称",    
"totalQuantity":"数量",    
"unitsNetListPrice":"标准价(RMB)"},

{"BOM":"0235A0W2",    
"typeID":"RT-MSR5660",   
"description":"H3C MSR 56-60路由器机框",   
"totalQuantity":"2",    
"unitsNetListPrice":"50000"}
]

我们来看一下特点:

  • 原始表格中的每一行转换为一个dict

  • 然后把所有行组成一个list
    image.png

从Excel中读数据

既然现在数据结构已经设计好了,我们就来看如何读数据,并形成这样的数据结构吧。

首先引入模块

import xlrd

然后定义一个操作类,从excel中读取数据并转换的函数为getAssociativeArray ,需要把Excel的完整路径传递进去,以及要读取的sheet的名称,还有就是为每列取的key值

class XlrdTool(XlsReader):
    # 作用:获取关联数组
    # inputHeaderKey:数组每一列的对应的键值
    # 返回:一个数组,数组的每一行为一个dict,代表原来表格里面的每一行,其中此dict的键名为输入的inputHeaderKey,键值为读入的excel文件的对应值。
    
    def getAssociativeArray (self, excelPathName, sheetName , inputHeaderKey):
        list = []
        try:
            sheetList = xlrd.open_workbook(excelPathName).sheet_by_name(sheetName);
            # row:表示从当前sheet读出了的每一行,
            # 将每一行的row_values与inputHeaderKey组成dict
            list = [dict (zip (inputHeaderKey , sheetList
            .row_values(row))) for row in range(sheetList.nrows)];
            
        except Exception as data:
            print("打开文件失败,%s" % data);
        return list;

里面最关键的代码其实只有:

list = []
 sheetList = xlrd.open_workbook(excelPathName).sheet_by_name(sheetName);
 list = [dict (zip (inputHeaderKey , sheetList
            .row_values(row))) for row in range(sheetList.nrows)];

我们来一一看看。

  • 从Excel中的某个sheet里面读出数据,xlrd.open_workbook(excelPathName).sheet_by_name(sheetName)

  • [dict (zip (inputHeaderKey , sheetList .row_values(row))) for row in range(sheetList.nrows)];我们可以拆解一下:

    • 这是一个列表生成式,for row in range(sheetList.nrows)表示对读出来的数组的每一行 row 进行遍历,

    对其中某一行row

    • 首先使用xlrd中的函数row_values取出每一行的值

    • inputHeaderKey表示为每一列赋予的一个key,它是一个数组。

    • 然后使用zip把读出来的每一行与inputHeaderKey组成键值对,再在外面迁套dict形成一个字典。
      也就是

{"BOM":"0235A0W2",    
"typeID":"RT-MSR5660",   
"description":"H3C MSR 56-60路由器机框",   
"totalQuantity":"2",    
"unitsNetListPrice":"50000"}
  • 所有的dict形成一个列表

总结一下就是,把为每列分配的key值数组与每一行进行zip,然后转换为dict,最后把所有的dict组成一个list。

image.png

数据结构的特点

上面讲了如何从Excel读取数据形成数组。

我们再来看一下这种数据结构

a = [
{"BOM":"产品编码",    
"typeID":"产品型号",    
"description":"项目名称",    
"totalQuantity":"数量",    
"unitsNetListPrice":"标准价(RMB)"},

{"BOM":"0235A0W2",    
"typeID":"RT-MSR5660",   
"description":"H3C MSR 56-60路由器机框",   
"totalQuantity":"2",    
"unitsNetListPrice":"50000"}
]

它是一个嵌套的数据结构,总体上是一个list,它有两个元素,每个元素都是一个dict

image.png

那么这个地方就有个坑点了

如果我们再把这个list赋给b,然后在b中把price的价格修改了。

image.png

那么list最开始指向的dict并没有变,没有指向另一个元素,所以list没有改变,

但是dict发生改变了。

也就是a对应的那个price也发生了改变了。

所以我们需要注意,如果把list赋给另一个变量以后,一定要深复制一份。

image.png

如何遍历

对于我们这个项目来说,遍历可能是最重要的算法了。首先我们来看一下我们官方给出来的表格吧。

一套配置清单其实有若干套设备构成,每套设备又有一个子标题以及相应的详细配置信息等,还有小计等。

多套设备组成了整个清单,

image.png

在讲遍历前,我们需要对每个区域取个名字。

加上colorTag

我们可以把所有行分为如下几类:

  • header:表示总的标题

  • site:表示每一套设备的子标题

  • subtotal:对每一套设备的小计

  • total:总计

  • general:详细配置信息

image.png

这几种他们对应的颜色也可以设为不同的,所以统称为colorTag

那么怎么在程序中区分不同的行的类型呢?

我们知道之前设计的数据结构本质就是一个list,而每一行是一个dict,所以只需要再加一个键值对即可,比如总计行就加上"colorTag":"total"即可。

之前读取Excel数据的时候并没有加上这个ColorTag,那么现在要加的话,需要对整个list进行一次遍历,识别每一行的特征,加上相应的colorTag

aDiff = [i for i in ['BOM','typeID','description']  if i in self.lists[0].keys()];
colTag = aDiff[0];
for aList in self.lists:
    if aList[colTag] == "小计":
        aList['colorTag'] = "subtotal";
    elif aList[colTag] == "总计":
        aList['colorTag'] = "total";
    elif aList['ID'] != "":
        aList['colorTag'] = 'site';
    else:
        aList['colorTag'] = "general";
        
self.lists[0]['colorTag'] = "header"

解释一下代码:

  • 首先colTag指的是'BOM','typeID','description'这几个谁存在,则取谁为colTag

  • 然后遍历数组的每一行,如果aList[colTag]="小计"或者踪迹的时候,就可以判断出是小计行或者总计行

  • 另外我们观察得到ID列除了子标题site对应的行有值,其他的行都是空的,所以可以使用aList['ID'] != ""来判断哪些是site行

  • 第一行就是header行

  • 剩下的自然是general

我们还可以再遍历一次新生成的list,然后把colorTag为site的那些行的序号取出来,这就可以确定每一套设备的起始和截止的位置了。

posted @ 2018-02-24 16:39  dy2903  阅读(165)  评论(0编辑  收藏  举报