I'm trying to turn on AutoFilter for the users who will consume the data.
open Microsoft.Office.Interop.Excel
let xl = ApplicationClass()
xl.Workbooks.OpenText(fileName...)
let wb = xl.Workbooks.Item(1)
let ws = wb.ActiveSheet :?> Worksheet
let rows = string ws.UsedRange.Rows.Count
// AutoFilter method of Range class failed.
ws.Range("A7:I" + rows).AutoFilter() |> ignore
Thanks for any help you can offer.
According to the documentation, you need to pass 5 parameters to AutoFilter.
Unspecified parameters can be filled by System.Reflection.Missing.Value.
Something like
ws.Range("A7:I" + rows).AutoFilter(1, System.Reflection.Missing.Value,
Excel.XlAutoFilterOperator.xlAnd,
System.Reflection.Missing.Value, true)
|> ignore
should work.
Related
I am trying to write an F# DataTable to csv (or output in a txt). The table I have is defined as follows:
let setup_report_tbl ( tbl : DataTable ) =
ignore( tbl.Columns.Add("business_date", typeof<System.Int32>) )
ignore( tbl.Columns.Add("ticker", typeof<System.String>) )
ignore( tbl.Columns.Add("price", typeof<System.String>) )
ignore( tbl.Columns.Add("rate", typeof<System.Boolean>) )
ignore( tbl.Columns.Add("range", typeof<System.Double>) )
My goal is to write this empty table with headers into a csv or txt. I'm new to F# and not quite sure where to start here, any help is appreciated thanks!
To write a DataTable as CSV, I would do something like this:
open System
open System.IO
open System.Data
let writeCsv (wtr : StreamWriter) (tbl : DataTable) =
let writeValues (values : seq<_>) =
String.Join(',', values)
|> wtr.WriteLine
tbl.Columns
|> Seq.cast<DataColumn>
|> writeValues
for row in tbl.Rows do
row.ItemArray |> writeValues
Note that I haven't done anything to check for special characters in the values, such commas or quotes.
Example:
let tbl = new DataTable()
setup_report_tbl tbl
tbl.Rows.Add(1, "moo", "baa", true, 2.0) |> ignore
use wtr = new StreamWriter(Console.OpenStandardOutput())
writeCsv wtr tbl
Output is:
business_date,ticker,price,rate,range
1,moo,baa,True,2
Update
To avoid compiler error, perhaps try this:
let writeValues (values : seq<_>) =
let s = String.Join(',', values)
wtr.WriteLine(s)
Note that s is a string, so there should be no ambiguity in which version of WriteLine is called.
If you wanted to use an existing library rather than writing your own CSV encoding (which may get tricky when you need to escape things), you could use Deedle which has an easy way to create data frame from a DataTable and save it to a CSV file:
#r "nuget: Deedle"
open Deedle
open System.Data
// Setup table using your function and add some data
let tbl = new DataTable()
setup_report_tbl tbl
tbl.Rows.Add(1, "very\",evil'ticker", "$42", false, 1.23)
// Turn it into a dataframe and save it
let df = Frame.ReadReader(tbl.CreateDataReader())
df.SaveCsv("C:/temp/test.csv")
As a bonus point, you could see if the data frame type from Deedle lets you do some of the other things you want to do with the data - but this depends on your scenario.
I want to return every value up to and including some key.
Whilst I could generate every such key and chuck them all into the Get, I suspect this will inefficiently search for the value of every key.
Inspired by this answer, I have come up with the following
let getAllUpTo key (frame:Frame<'key,'col>) : Frame<'key, 'col> =
let endRng = frame.RowIndex.Locate key
let startRng = frame.RowIndex.KeyRange |> fst |> frame.RowIndex.Locate
let fixedRange = RangeRestriction.Fixed (startRng, endRng)
frame.GetAddressRange fixedRange
Is there a built in method for doing this efficiently?
If you want to access a sub-range of a data frame with a specified starting/ending key, you can do this using the df.Rows.[ ... ] indexer. Say we have some data indexed by (sorted) dates:
let s1 = series [
let rnd = Random()
for d in 0 .. 365 ->
DateTime(2020, 1, 1).AddDays(float d) => rnd.Next() ]
let df = frame [ "S1" => s1 ]
To get a part of the data frame starting/ending on a specific date, you can use:
// Get all rows from 1 June (inclusive)
df.Rows.[DateTime(2020, 6, 1) ..]
// Get all rows until 1 June (inclusive)
df.Rows.[.. DateTime(2020, 6, 1)]
The API you are using is essentially what this does under the cover - but you are using a very low-level operations that you do not typically need to use in user code.
i am new with f# , will be great if some 1 can help , nearly half a day gone solving this problem Thank you
module Certificate =
type T = {
Id: int
IsECert: bool
IsPrintCert: bool
CertifiedBy: string
Categories: Category.T list
}
let createPending now toZonedDateTime toBeCertifiedByName (job: Models.Job.T) (certificateType: Models.CertificateType.T) (pendingCertificate: Models.PendingCertificate.T) visualization (categories: Category.T list) =
let forCompletion = Models.PendingCertificate.getCertificateForCompletion pendingCertificate
{
Id = forCompletion.Id |> CertificateId.toInt
IsECert = Models.PendingCertificate.isECertificate pendingCertificate
IsPrintCert = Models.PendingCertificate.isPrintCertificate pendingCertificate
CertifiedBy = toBeCertifiedByName
Categories = categories}
i am getting an error in "Incomplete structured construct at or before this point"
Your formatting is all off. I will assume here that this is just a result of posting to StackOverflow, and your actual code is well indented.
The error comes from the definition of createPending: this function does not have a result. All its body consists of defining a forCompletion value, but there is nothing after it. Here's a simpler example that has the same problem:
let f x =
let y = 5
This function will produce the same error, because it also doesn't have a result. In F#, every function has to return something. The body cannot contain only definitions of helper functions or values. For example, I could fix my broken function above like this:
let f x =
let y = 5
x + y
This function first defines a helper value y, then adds it to its argument x, and returns the result.
> f 2
> 7
>
> f 0
> 5
How exactly you need to fix your function depends on what exactly you want it to mean. I can't help you here, because you haven't provided that information.
Thanks to everybody who has helped over the past few months trying to help me get my silverlight / f# prototype up and running (started in the RC version of VS - Ugh). The last problem we are trying to solve is an RPC issue.
We need to have the ability to paginate RPC calls, such that the first page is requested and bound to the grid and displayed, while the otehr pages are prefilled in the background and concatenated together. I guess psuedo code would look like this:
let pageNo = 1
let page1Data = JsonRpc.getSomeData(pageNo)
let grid.datasource <- page1Data
let grid.suspendFiltering <- true
// run the remainder in background
let allData : list ref = page1Data ref
for pageNo in [2..totalPages]
allData := allData # JsonRpc.getSomeData(pageNo)
let grid.datasource <- allData
let grid.suspendFiltering <- true
I appologize for the code above, I tried to make it as F# like as possible (writing in this text window); another flaw is the need to use call backs to bind the data to grids etc.
The question approaches might be used to solve this problem and what is the most approriate?
hmm... something like this? (typing in browser so may contain errors):
module Loader
open System
open System.Threading
let totalPages = 20
// emulation of long-running data loading routine
let private loadPageData (page : int) =
async {
do! Async.Sleep(1000)
return List.replicate 5 page
}
// loader - notifies UI about new data via callback
let loadAsync (callback : System.Action<_>) =
let syncContext = SynchronizationContext.Current
let doLoad = async {
// load first page and immediately feed it to callback
let! page1Data = loadPageData 1
do! Async.SwitchToContext syncContext
callback.Invoke(ResizeArray<_>(page1Data))
// load remaining data in the background
do! Async.SwitchToThreadPool()
let allData = ResizeArray<_>(page1Data)
for page in 2..totalPages do
let! pageData = loadPageData page
allData.AddRange(pageData)
do! Async.SwitchToContext syncContext
callback.Invoke(allData)
}
Async.Start doLoad
On UI side it will appear like this (i.e. data - ListBox or some other control)
Loader.loadDataAsync(list => data.ItemSource = list)
I would like to display a directory structure using Gtk# widgets through F#, but I'm having a hard time figuring out how to translate TreeViews into F#. Say I had a directory structure that looks like this:
Directory1
SubDirectory1
SubDirectory2
SubSubDirectory1
SubDirectory3
Directory2
How would I show this tree structure with Gtk# widgets using F#?
EDIT:
gradbot's was the answer I was hoping for with a couple of exceptions. If you use ListStore, you loose the ability to expand levels, if you instead use :
let musicListStore = new Gtk.TreeStore([|typeof<String>; typeof<String>|])
you get a layout with expandable levels. Doing this, however, breaks the calls to AppendValues so you have to add some clues for the compiler to figure out which overloaded method to use:
musicListStore.AppendValues (iter, [|"Fannypack" ; "Nu Nu (Yeah Yeah) (double j and haze radio edit)"|])
Note that the columns are explicitly passed as an array.
Finally, you can nest levels even further by using the ListIter returned by Append Values
let iter = musicListStore.AppendValues ("Dance")
let subiter = musicListStore.AppendValues (iter, [|"Fannypack" ; "Nu Nu (Yeah Yeah) (double j and haze radio edit)"|])
musicListStore.AppendValues (subiter, [|"Some Dude"; "Some Song"|]) |> ignore
I'm not exactly sure what you're looking for but here is a translated example from their tutorials. It may help you get started. Image taken from tutorial site.
I think the key to a multi-level tree view is to append values to values, iter in this line musicListStore.AppendValues (iter, "Fannypack", "Nu Nu (Yeah Yeah) (double j and haze radio edit)") |> ignore
// you will need to add these references gtk-sharp, gtk-sharp, glib-sharp
// and set the projects running directory to
// C:\Program Files (x86)\GtkSharp\2.12\bin\
module SOQuestion
open Gtk
open System
let main() =
Gtk.Application.Init()
// Create a Window
let window = new Gtk.Window("TreeView Example")
window.SetSizeRequest(500, 200)
// Create our TreeView
let tree = new Gtk.TreeView()
// Add our tree to the window
window.Add(tree)
// Create a column for the artist name
let artistColumn = new Gtk.TreeViewColumn()
artistColumn.Title <- "Artist"
// Create the text cell that will display the artist name
let artistNameCell = new Gtk.CellRendererText()
// Add the cell to the column
artistColumn.PackStart(artistNameCell, true)
// Create a column for the song title
let songColumn = new Gtk.TreeViewColumn()
songColumn.Title <- "Song Title"
// Do the same for the song title column
let songTitleCell = new Gtk.CellRendererText()
songColumn.PackStart(songTitleCell, true)
// Add the columns to the TreeView
tree.AppendColumn(artistColumn) |> ignore
tree.AppendColumn(songColumn) |> ignore
// Tell the Cell Renderers which items in the model to display
artistColumn.AddAttribute(artistNameCell, "text", 0)
songColumn.AddAttribute(songTitleCell, "text", 1)
let musicListStore = new Gtk.ListStore([|typeof<String>; typeof<String>|])
let iter = musicListStore.AppendValues ("Dance")
musicListStore.AppendValues (iter, "Fannypack", "Nu Nu (Yeah Yeah) (double j and haze radio edit)") |> ignore
let iter = musicListStore.AppendValues ("Hip-hop")
musicListStore.AppendValues (iter, "Nelly", "Country Grammer") |> ignore
// Assign the model to the TreeView
tree.Model <- musicListStore
// Show the window and everything on it
window.ShowAll()
// add event handler so Gtk will exit
window.DeleteEvent.Add(fun _ -> Gtk.Application.Quit())
Gtk.Application.Run()
[<STAThread>]
main()