2

我正在使用 Visual Studio 2010 中的 Excel 互操作来尝试按字母顺序对所有这些数据行进行排序。有些已经按字母顺序排列。

Accountancy Graduate, Trainees  Banking, Insurance, Finance
Accountancy Graduate, Trainees  Customer Services
Accountancy Graduate, Trainees  Education
Accountancy Graduate, Trainees  Health, Nursing
Accountancy Graduate, Trainees  Legal
Accountancy Graduate, Trainees  Management Consultancy
Accountancy Graduate, Trainees  Media, New Media, Creative
Accountancy Graduate, Trainees  Oil, Gas, Alternative Energy
Accountancy Graduate, Trainees  Public Sector & Services
Accountancy Graduate, Trainees  Recruitment Sales
Accountancy Graduate, Trainees  Secretarial, PAs, Administration
Accountancy Graduate, Trainees  Telecommunications
Accountancy Graduate, Trainees  Transport, Logistics

我的代码的当前版本如下(我让我的代码在将其放入 fs 文件之前以交互方式工作)。

#r "office.dll"
#r "Microsoft.Office.Interop.Excel.dll"

open System;;
open System.IO;;
open Microsoft.Office.Interop.Excel;;




let app = new ApplicationClass(Visible = true)
let inputBook = app.Workbooks.Open @"C:\Users\simon.hayward\Dropbox\F# Scripts\TotalJobsSort\SortData.xlsx" //work
//let inputBook = app.Workbooks.Open @"C:\Users\Simon Hayward\Dropbox\F# Scripts\TotalJobsSort\SortData.xlsx"  //home
let outputBook = app.Workbooks.Add()
let inSheet = inputBook.Worksheets.[1] :?> _Worksheet
let outSheet = outputBook.Worksheets.[1] :?> _Worksheet

let rows = inSheet.UsedRange.Rows.Count;;

let toSeq (range : Range) =
      seq {
           for r in 1 .. range.Rows.Count do
                  for c in 1 .. range.Columns.Count do
                      let cell = range.Item(r, c) :?> Range
                      yield cell
              }
for i in 1 .. rows do 

      let mutable row = inSheet.Cells.Rows.[i] :?> Range

      row |> toSeq |> Seq.map (fun x -> x.Value2.ToString()) |> Seq.sort |> 

     (outSheet.Cells.Rows.[i] :?> Range).Value2 <- row.Value2;;



app.Quit();;

但是类型有问题。退出命令前的最后一行

(outSheet.Cells.Rows.[i] :?> Range).Value2 <- row.Value2;;

智能感知用红色下划线,我得到的错误是

“这个表达式应该有类型 seq -> 'a 但这里有类型 unit”。

我得到了 VS 试图告诉我的内容,但我现在已经多次尝试解决这个问题,但我似乎无法解决类型问题。

谁能告诉我如何将管道设置为正确的类型,以便输出写入我的输出表?

编辑 1:这是我得到的完整错误消息,其中 sorted 变量注释如下

let sorted = row |> toSeq //|> Seq.map (fun x -> x.Value2.ToString()) |> Seq.sort

错误信息是:-

System.Runtime.InteropServices.COMException (0x800A03EC):来自 HRESULT 的异常:System.RuntimeType.ForwardCallToInvokeMember 的 0x800A03EC(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData) at Microsoft.Office.Interop.Excel.Range .get_Item(Object RowIndex, Object ColumnIndex) at FSI_0122.toSeq@34-47.Invoke(Int32 c) in C:\Users\Simon Hayward\Dropbox\F# Scripts\TotalJobsSort\sortExcelScript.fsx:Microsoft.FSharp 的第 36 行。 Collections.IEnumerator.map@109.DoMoveNext(b& ) 在 Microsoft.FSharp.Collections.IEnumerator.MapEnumerator 1.System-Collections-IEnumerator-MoveNext() at Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers.takeOuter@651[T,TResult](ConcatEnumerator2 x,单位 unitVar0) 在 Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers.takeInner@644[T,TResult](ConcatEnumerator 2 x, Unit unitVar0) at <StartupCode$FSharp-Core>.$Seq.MoveNextImpl@751.GenerateNext(IEnumerable1&下一个)在 Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase1.MoveNextImpl() at Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase1.System-Collections-IEnumerator-MoveNext() at Microsoft.FSharp.Collections.SeqModule.ToArray[T](IEnumerable 1 source) at Microsoft.FSharp.Collections.ArrayModule.OfSeq[T](IEnumerable1 source) at .$FSI_0122.main@() in C:\Users\Simon Hayward\Dropbox\F# Scripts\TotalJobsSort\sortExcelScript.fsx:line 42 由于错误而停止

编辑 2:这个问题可能是由于 toSeq 函数被设计为将整个工作表变成一个序列吗?在我应用它的地方,我只希望它适用于一行。

我尝试将 toSeq 中的 r 变量限制为 1,但这没有帮助。

我的实际数据是锯齿状数组这一事实是否重要?它并不总是每行有 3 个条目,它在 1 到 4 之间变化。

编辑 3:

这是我的代码的当前迭代,基于 Tomas 的建议

#r "office.dll"
#r "Microsoft.Office.Interop.Excel.dll"

open System;;
open System.IO;;

open Microsoft.Office.Interop.Excel;;



let app = new ApplicationClass(Visible = true);;
let inputBook = app.Workbooks.Open @"SortData.xlsx" //workbook
let outputBook = app.Workbooks.Add();;
let inSheet = inputBook.Worksheets.[1] :?> _Worksheet
let outSheet = outputBook.Worksheets.[1] :?> _Worksheet

let rows = inSheet.UsedRange.Rows.Count;;
let columns = inSheet.UsedRange.Columns.Count;;

// Get the row count and calculate the name of the last cell e.g. "A13"
let rangeEnd = sprintf "A%d" columns

// Get values in the range A1:A13 as 2D object array of size 13x1
let values = inSheet.Range("A1", rangeEnd).Value2 :?> obj[,]

// Read values from the first (and only) column into 1D string array
let data = [| for i in 1 .. columns -> values.[1, i] :?> string |]
// Sort the array and get a new sorted 1D array
let sorted1D = data |> Array.sort
// Turn the 1D array into 2D array (13x1), so that we can write it back
let sorted2D = Array2D.init 1 columns (fun i _ -> data.[i])

// Write the data to the output sheet in Excel
outSheet.Range("A1", rangeEnd).Value2 <- sorted2D

但是因为实际数据在每一行中都有可变数量的条目,所以我得到了标准范围异常错误(这至少是对过去几天的 HRESULT 异常错误的改进)。

所以我需要为每一行定义列,或者只是将行的长度绑定到 for 循环中的一个变量。(我猜)。

4

1 回答 1

4

看起来您|>在行尾有一个附加运算符Seq.sort- 这意味着列表已排序,然后编译器尝试将其传递给执行赋值的表达式(它不接受任何参数并且具有类型unit)。

应该编译这样的东西(尽管可能存在其他一些运行时问题):

for i in 1 .. rows do 
  let row = inSheet.Cells.Rows.[i] :?> Range
  let sorted = row |> toSeq |> Seq.map (fun x -> x.Value2.ToString()) |> Seq.sort
  (outSheet.Cells.Rows.[i] :?> Range).Value2 <- Array.ofSeq sorted

请注意,您不需要标记row为可变,因为代码会创建一个副本(并且 - 在我的版本中 - 将其分配给一个新变量sorted)。

我还使用Array.ofSeq将排序后的序列转换为数组,因为我认为 Excel 互操作更适用于数组。

在范围上设置Value2属性时,范围的大小应与您分配给它的数组的大小相同。此外,根据您要设置的范围,您可能需要一个二维数组。

编辑关于运行时错误,我不完全确定您的代码有什么问题,但这是我将如何进行排序(假设您只有一列带有字符串值并且您想要对行进行排序):

// Get the row count and calculate the name of the last cell e.g. "A13"
let rows = inSheet.UsedRange.Rows.Count
let rangeEnd = sprintf "A%d" rows

// Get values in the range A1:A13 as 2D object array of size 13x1
let values = inSheet.Range("A1", rangeEnd).Value2 :?> obj[,]
// Read values from the first (and only) column into 1D string array
let data = [| for i in 1 .. rows -> values.[i, 1] :?> string |]
// Sort the array and get a new sorted 1D array
let sorted1D = data |> Array.sort
// Turn the 1D array into 2D array (13x1), so that we can write it back
let sorted2D = Array2D.init rows 1 (fun i _ -> data.[i])

// Write the data to the output sheet in Excel
outSheet.Range("A1", rangeEnd).Value2 <- sorted
于 2013-03-05T13:07:14.870 回答