Note that there are some explanatory texts on larger screens.

plurals
  1. POSorting Excel rows alphabetically in F# (Office.Interop)
    primarykey
    data
    text
    <p>I am using the Excel interop in Visual Studio 2010 to try to sort all of these rows of data alphabetically. Some are already in alphabetical order.</p> <pre><code>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 &amp; Services Accountancy Graduate, Trainees Recruitment Sales Accountancy Graduate, Trainees Secretarial, PAs, Administration Accountancy Graduate, Trainees Telecommunications Accountancy Graduate, Trainees Transport, Logistics </code></pre> <p>The current version of my code is as follows (I'm getting my code to work in interactive before putting it into an fs file). </p> <pre><code>#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] :?&gt; _Worksheet let outSheet = outputBook.Worksheets.[1] :?&gt; _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) :?&gt; Range yield cell } for i in 1 .. rows do let mutable row = inSheet.Cells.Rows.[i] :?&gt; Range row |&gt; toSeq |&gt; Seq.map (fun x -&gt; x.Value2.ToString()) |&gt; Seq.sort |&gt; (outSheet.Cells.Rows.[i] :?&gt; Range).Value2 &lt;- row.Value2;; app.Quit();; </code></pre> <p>But there is a problem with types. The final line before the quit command</p> <pre><code>(outSheet.Cells.Rows.[i] :?&gt; Range).Value2 &lt;- row.Value2;; </code></pre> <p>Is red underlined by intellisense and the error I get is</p> <p>"This expression is expected to have type seq -> 'a but here has type unit".</p> <p>I get what VS is trying to tell me, but I have made several attempts to fix this now and i can't seem to get around the type issue.</p> <p>Can anyone please advise how I can get the pipeline to the correct type so that the output will write to my output sheet?</p> <p>EDIT 1: This is the full error message that I get with the sorted variable commented out as follows</p> <pre><code>let sorted = row |&gt; toSeq //|&gt; Seq.map (fun x -&gt; x.Value2.ToString()) |&gt; Seq.sort </code></pre> <p>The error message is:-</p> <blockquote> <p>System.Runtime.InteropServices.COMException (0x800A03EC): Exception from HRESULT: 0x800A03EC at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData&amp; 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:line 36 at Microsoft.FSharp.Collections.IEnumerator.map@109.DoMoveNext(b&amp; ) at Microsoft.FSharp.Collections.IEnumerator.MapEnumerator<code>1.System-Collections-IEnumerator-MoveNext() at Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers.takeOuter@651[T,TResult](ConcatEnumerator</code>2 x, Unit unitVar0) at Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers.takeInner@644[T,TResult](ConcatEnumerator<code>2 x, Unit unitVar0) at &lt;StartupCode$FSharp-Core&gt;.$Seq.MoveNextImpl@751.GenerateNext(IEnumerable</code>1&amp; next) at Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase<code>1.MoveNextImpl() at Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase</code>1.System-Collections-IEnumerator-MoveNext() at Microsoft.FSharp.Collections.SeqModule.ToArray[T](IEnumerable<code>1 source) at Microsoft.FSharp.Collections.ArrayModule.OfSeq[T](IEnumerable</code>1 source) at .$FSI_0122.main@() in C:\Users\Simon Hayward\Dropbox\F# Scripts\TotalJobsSort\sortExcelScript.fsx:line 42 Stopped due to error</p> </blockquote> <p>EDIT 2: Could this problem be due to the toSeq function being designed to turn a whole sheet into a sequence? Where I apply it I only want it to apply to one row.</p> <p>I have tried limiting the r variable in toSeq to 1, but this didn't help.</p> <p>Does the fact that my actual data is a jagged array matter? It does not always have 3 entries in each row, it varies between 1 and 4.</p> <p>EDIT 3:</p> <p>Here is the current iteration of my code, based on Tomas' suggestions</p> <pre><code>#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] :?&gt; _Worksheet let outSheet = outputBook.Worksheets.[1] :?&gt; _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 :?&gt; obj[,] // Read values from the first (and only) column into 1D string array let data = [| for i in 1 .. columns -&gt; values.[1, i] :?&gt; string |] // Sort the array and get a new sorted 1D array let sorted1D = data |&gt; 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 _ -&gt; data.[i]) // Write the data to the output sheet in Excel outSheet.Range("A1", rangeEnd).Value2 &lt;- sorted2D </code></pre> <p>But because the actual data has a variable number of entries in each row I am getting the standard range exception error (this is an improvement on the HRESULT exception errors of the last few days at least).</p> <p>So I need to define columns for each individual row, or just bind the length of the row to a variable in the for loop. (I would guess).</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload