Сортировка строк Excel по алфавиту в F# (Office.Interop)

Я использую взаимодействие Excel в Visual Studio 2010, чтобы попытаться отсортировать все эти строки данных в алфавитном порядке. Некоторые уже в алфавитном порядке.

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();;

Но есть проблема с типами. Последняя строка перед командой quit

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

Красная линия подчеркнута intellisense, и ошибка, которую я получаю,

Msgstr "Это выражение, как ожидается, будет иметь тип seq -> 'a, но здесь имеет тип unit".

Я понимаю, что VS пытается сказать мне, но я сделал несколько попыток исправить это сейчас, и я не могу обойти проблему с типом.

Кто-нибудь может посоветовать, пожалуйста, как я могу получить конвейер правильного типа, чтобы выходные данные записывались в мой выходной лист?

РЕДАКТИРОВАТЬ 1: Это полное сообщение об ошибке, которое я получаю с отсортированной переменной, закомментированной следующим образом

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

Сообщение об ошибке:

System.Runtime.InteropServices.COMException (0x800A03EC): Исключение из HRESULT: 0x800A03EC в System.RuntimeType.ForwardCallToInvokeMember(строковые члены memberName, BindingFlags, объектная цель, Int32[] aWrapperTypes, msD.ExD.Gence.InDataGateInInter_D_G_D_D_G_D_G_D_G_D_G_P_D_G_P_G_P_P_P_P_P_P_P_S_S_P_S_S_S_S_S_S_S_S_S_S_S_S2.get_Item(Object RowIndex, Object ColumnIndex) в FSI_0122.toSeq@34-47.Invoke(Int32 c) в C:\Users\Simon Hayward\Dropbox\F# Скрипты \TotalJobsSort\sortExcelScript.fsx: строка 36 в Microsoft.FSharp.Collections.IEnumerator.map@109.DoMoveNext(b&) в Microsoft.FSharp.Collections.IEnumerator.MapEnumerator1.System-Collections-IEnumerator-MoveNext() at Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers.takeOuter@651[T,TResult](ConcatEnumerator2 x, Unit unitVar0) в Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers.takeInner@644 [T, TResult] (ConcatEnumerator2 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 () в Microsoft.FSharp.Collections.SeqModule.ToArray [T] (IEnumerable1 source) at Microsoft.FSharp.Collections.ArrayModule.OfSeq[T](IEnumerable1 источник) at .$FSI_0122.main@() в C:\Users\Simon Hayward\Dropbox\F# Scripts\TotalJobsSort\sortExcelScript.fsx: строка 42 Остановлена ​​из-за ошибки

РЕДАКТИРОВАТЬ 2: Может ли эта проблема быть из-за функции toSeq, предназначенной для превращения целого листа в последовательность? Где я применяю это, я только хочу, чтобы это применялось к одному ряду.

Я попытался ограничить переменную r в toSeq до 1, но это не помогло.

Имеет ли значение тот факт, что мои фактические данные являются зубчатыми массивами? Он не всегда имеет 3 записи в каждой строке, он варьируется от 1 до 4.

РЕДАКТИРОВАТЬ 3:

Вот текущая итерация моего кода, основанная на предложениях Томаса

#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. (Я бы догадался).

1 ответ

Похоже, у вас есть дополнительный |> оператор в конце строки с 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
Другие вопросы по тегам