首页 > 解决方案 > 在 F# 中解析中间有孔的列表的最惯用方法

问题描述

我正在解析一个以这种方式组织的 excel 文件:

header1
header2
data
data
[...]
data
one blank line
data
data
[...]
data
one blank line

所以,我们有一个标题,要跳过,一些可变长度的数据,一个空白行,一些可变长度的数据和一个空白行来标记感兴趣区域的结束。

这两个数据块以相同的方式解析,它们最终必须在一个列表中,但重要的是我知道中间空白行的索引(它是一个从中间处理的列表,要么朝向顶部或朝向底部)。

有两个警告:

现在,我有一个不太干净的实现,因为它重复了代码:

let gridRowsUp =
    gridExcel
    |> List.skip 2
    |> List.takeWhile (fun rowData       -> rowData |> Seq.exists(fun x -> not (String.IsNullOrEmpty(x))))
    |> List.mapi      (fun index rowData -> parseGridLayer (index + 2) rowData)

// get the index of the middle row
let middleRow = 2 + gridRowsUp.Length

// get the bottom part of the grid
let gridRowsDown =
    gridExcel
    |> List.skip      (1 + middleRow)
    |> List.takeWhile (fun rowData       -> rowData |> Seq.exists(fun x -> not (String.IsNullOrEmpty(x))))
    |> List.mapi      (fun index rowData -> parseGridLayer (index + 1 + middleRow) rowData)

let gridData = gridRowsUp @ gridRowsDown

理想情况下,我想一次性处理数据线,但跳过并记录中间空白行的位置。

我想过找到第一个空行并在没有它的情况下重建一个列表(因为我现在知道索引),但它很复杂,因为我需要找到第一个,然后找到第二个以知道在哪里停止(可以有一个下面有很多未使用的额外行)然后再次构建一个列表。在 F# 中删除列表中间的元素并不理想。

我怎样才能使它更精简?

标签: f#

解决方案


我认为循环会比重复代码更好。这段代码应该阐明逻辑。

// main dataset
let gridExcel = [
"header1";  "header2";   "data1";    "data2";    "data3";    "data4";    "";
"data5";    "data6";     "";
"data7";    "data8";     "data9";    "data10";    "";
"data11";   "data12";    "data13";   "data14";   "data15";    "";
"data16";   "data17"
]

let mutable blanks = []  // index of blank lines
let mutable allrows = []  // all non-blanks rows (no headers)

let mutable ctr = 2  // row index of main data
let mutable run = true

while (run) do
   let gridRows = 
       gridExcel
       |> List.skip ctr  // skip previous data
       |> List.takeWhile (fun rowData       -> not (String.IsNullOrEmpty(rowData)))  // while not empty line
       |> List.mapi      (fun index rowData -> "xx" + rowData)   // process entry
   allrows <- allrows@gridRows    // add to list total
   ctr <- ctr + gridRows.Length   // add to skip ctr
   if (ctr < gridExcel.Length) then blanks <- blanks@[ctr]   // store blank line index
   ctr <- ctr + 1  // skip blank line
   if (ctr >= gridExcel.Length) then run <- false  // done main list
   
printfn "Blanks: %A\n" blanks   // indexes of blanks lines
printfn "Processed Data:\n%A" allrows  // all processed rows

输出

Blanks: [6; 9; 14; 20]

Processed Data:
["xxdata1"; "xxdata2"; "xxdata3"; "xxdata4"; "xxdata5"; "xxdata6"; "xxdata7";
 "xxdata8"; "xxdata9"; "xxdata10"; "xxdata11"; "xxdata12"; "xxdata13";
 "xxdata14"; "xxdata15"; "xxdata16"; "xxdata17"]

推荐阅读