2013-03-05 5 views
2

J'utilise Excel interop dans Visual Studio 2010 pour essayer de trier toutes ces lignes de données par ordre alphabétique. Certains sont déjà dans l'ordre alphabétique. La version actuelle de mon code est la suivante (je commence à utiliser mon code interactif avant de le placer dans un fichier fs).Tri des lignes Excel par ordre alphabétique dans F # (Office.Interop)

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

Mais il y a un problème avec les types. La dernière ligne avant la quit

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

est rouge soulignée par IntelliSense et l'erreur que je reçois est

« Cette expression devrait avoir le type suivants -> « a, mais ici est de type unit ». Je comprends ce que VS essaie de me dire, mais j'ai essayé plusieurs fois de résoudre ce problème maintenant et je n'arrive pas à contourner le problème du type.

Quelqu'un peut-il s'il vous plaît aviser comment je peux obtenir le pipeline au bon type afin que la sortie écrira à ma feuille de sortie?

EDIT 1: Ceci est le message d'erreur complet que je reçois avec la variable triée a commenté comme suit

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

Le message d'erreur est le suivant: -

System.Runtime.InteropServices.COMException (0x800A03EC): Exception from HRESULT: 0x800A03EC at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData) at Microsoft.Office.Interop.Excel.Range.get_Item(Object RowIndex, Object ColumnIndex) at [email protected](Int32 c) in C:\Users\Simon Hayward\Dropbox\F# Scripts\TotalJobsSort\sortExcelScript.fsx:line 36 at [email protected](b&) at Microsoft.FSharp.Collections.IEnumerator.MapEnumerator 1.System-Collections-IEnumerator-MoveNext() at [email protected]651[T,TResult](ConcatEnumerator 2 x, Unit unitVar0) at [email protected]644[T,TResult](ConcatEnumerator 2 x, Unit unitVar0) at <StartupCode$FSharp-Core>[email protected](IEnumerable 1& next) at Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase 1.MoveNextImpl() at Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase 1.System-Collections-IEnumerator-MoveNext() at Microsoft.FSharp.Collections.SeqModule.ToArray[T](IEnumerable 1 source) at Microsoft.FSharp.Collections.ArrayModule.OfSeq[T](IEnumerable 1 source) at [email protected]() in C:\Users\Simon Hayward\Dropbox\F# Scripts\TotalJobsSort\sortExcelScript.fsx:line 42 Stopped due to error

EDIT 2: Serait-ce problème est-ce dû au fait que la fonction toSeq est conçue pour transformer une feuille entière en séquence? Lorsque je l'applique, je veux seulement qu'il s'applique à une rangée.

J'ai essayé de limiter la variable r à toSeq à 1, mais cela n'a pas aidé.

Est-ce que le fait que mes données réelles sont en dents de scie? Il n'a pas toujours 3 entrées dans chaque rangée, il varie entre 1 et 4.

EDIT 3:

Voici l'itération actuelle de mon code, basé sur les suggestions de 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 

Mais parce que les données réelles ont un nombre variable d'entrées dans chaque rangée j'obtiens l'erreur d'exception de gamme standard (c'est une amélioration sur les erreurs d'exception HRESULT des derniers jours au moins).

Je dois donc définir des colonnes pour chaque ligne individuelle, ou simplement lier la longueur de la ligne à une variable de la boucle for. (Je dirais).

+0

pour noter: Si je commente sur cette ligne et à la place imprimer à la console: ligne |> toSeq |> Seq.map (fun x -> x.Value2.ToString()) |> Seq.sort |> printf "% A" ;; Je reçois l'erreur suivante pour chaque ligne: - Erreur: Exception de HRESULT: 0x800A03EC qui est une erreur de plage pour Excel. Alors peut-être que la faute est dans l'énumération dans la fonction toSeq ?? –

Répondre

4

Il semble que vous avez un opérateur |> supplémentaire à la fin de la ligne avec Seq.sort - cela signifie que la liste est triée puis, le compilateur essaie de le transmettre à l'expression qui effectue la mission (qui ne prend pas n'importe quel paramètre et a un type unit).

Quelque chose comme cela devrait compiler (mais il peut y avoir d'autres problèmes d'exécution):

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 

Notez que vous n'avez pas besoin de marquer row mutable, car le code crée une copie (et - dans mon version - l'affecte à une nouvelle variable sorted).

J'utilise également Array.ofSeq pour convertir la séquence triée en tableau, car je pense que l'interopérabilité Excel fonctionne mieux avec les tableaux. Lorsque vous définissez la propriété Value2 sur une plage, la taille de la plage doit être la même que la taille de la baie que vous lui attribuez. En outre, en fonction de la plage que vous souhaitez définir, vous pouvez avoir besoin d'un tableau 2D.

EDIT En ce qui concerne les erreurs d'exécution, je ne suis pas tout à fait sûr de ce qui ne va pas avec votre code, mais voici comment je ferais le tri (en supposant que vous avez une seule colonne avec des valeurs de chaîne et que vous voulez trier les lignes):

// 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 
+0

Tomas - merci. Malheureusement quand je fais les changements ci-dessus au code j'obtiens toujours l'erreur d'exception de gamme: - "System.Runtime.InteropServices.COMException (0x800A03EC): Exception de HRESULT: 0x800A03EC ....." Le message d'erreur complet est trop long poster ici. –

+0

Mais encore une fois je pense que je suis un peu plus proche! –

+0

@SimonHayward Hmm, je suppose que cela a à voir avec la taille mésappariée de la gamme. Je n'ai trouvé aucun moyen de déboguer cela, mais vous pouvez essayer de simplifier le code - d'abord obtenir une plage représentant une seule cellule et essayer d'y écrire une valeur, puis essayer un tableau 1D etc. –

Questions connexes