Im trying to learn F#.
I want to take no more elements from Seq (or array) than a constant.
I use this code: [ "11"; "12"; "13" ] |> Seq.take 2 |> Seq.toList |> Seq.iter (printf "%A ") I get "11" "12"
If I try [ "11"; "12"; "13" ] |> Seq.take 4 |> Seq.toList |> Seq.iter (printf "%A ") I get an exception like System.InvalidOperationException: The input sequence has an insufficient number of elements.
I can think of takeWhile as in [ "11"; "12"; "13" ] |> Seq.takeWhile (fun elem -> true) |> Seq.toList |> Seq.iter (printf "%A ")
But I have no idea how to stop taking on reaching some constant limit.
So I need something like [ "11"; "12"; "13" ] |> Seq.takeNoMoreThan 4 |> Seq.toList |> Seq.iter (printf "%A ").
I have no idea how to achieve my goal.
You should use Seq.truncate instead.
Related
I am trying to use FSharp.Data's HTML Parser to extract a string List of links from href attributes.
I can get the links printed out to console, however, i'm struggling to get them into a list.
Working snippet of a code which prints the wanted links:
let results = HtmlDocument.Load(myUrl)
let links =
results.Descendants("td")
|> Seq.filter (fun x -> x.HasClass("pagenav"))
|> Seq.map (fun x -> x.Elements("a"))
|> Seq.iter (fun x -> x |> Seq.iter (fun y -> y.AttributeValue("href") |> printf "%A"))
How do i store those strings into variable links instead of printing them out?
Cheers,
On the very last line, you end up with a sequence of sequences - for each td.pagenav you have a bunch of <a>, each of which has a href. That's why you have to have two nested Seq.iters - first you iterate over the outer sequence, and on each iteration you iterate over the inner sequence.
To flatten a sequence of sequences, use Seq.collect. Further, to convert a sequence to a list, use Seq.toList or List.ofSeq (they're equivalent):
let a = [ [1;2;3]; [4;5;6] ]
let b = a |> Seq.collect id |> Seq.toList
> val b : int list = [1; 2; 3; 4; 5; 6]
Applying this to your code:
let links =
results.Descendants("td")
|> Seq.filter (fun x -> x.HasClass("pagenav"))
|> Seq.map (fun x -> x.Elements("a"))
|> Seq.collect (fun x -> x |> Seq.map (fun y -> y.AttributeValue("href")))
|> Seq.toList
Or you could make it a bit cleaner by applying Seq.collect at the point where you first encounter a nested sequence:
let links =
results.Descendants("td")
|> Seq.filter (fun x -> x.HasClass("pagenav"))
|> Seq.collect (fun x -> x.Elements("a"))
|> Seq.map (fun y -> y.AttributeValue("href"))
|> Seq.toList
That said, I would rather rewrite this as a list comprehension. Looks even cleaner:
let links = [ for td in results.Descendants "td" do
if td.HasClass "pagenav" then
for a in td.Elements "a" ->
a.AttributeValue "href"
]
How do I identify the max length from a Map's value set?
let numbers = [1;2;2;3;3;3;4;5;5]
let map = numbers |> Seq.groupBy id
|> Map.ofSeq
I want to do this:
map.Values |> List.max
or...
let longestSequence = Map.map (fun (k, v) -> List.max(List.ofSeq(v)));
you can get something similar to Dictionary.Values with Map.toSeq >> Seq.map snd so you can get the largest collected sequence in your map like this:
> map |> Map.toSeq |> Seq.map snd |> Seq.maxBy Seq.length;;
val it : seq<int> = seq [3; 3; 3]
of course when your list is already in a sorted stage it seems strange to take the detour over Map as
> numbers |> Seq.groupBy id |> Seq.map snd |> Seq.maxBy Seq.length;;
val it : seq<int> = seq [3; 3; 3]
will do the same ;)
also if you think about the problem here can write a List.fold (with a additional map of the result) doing this as well which will only require to traverse the (sorted) list once ... maybe you can try to do this yourself ^^
In order to get comfortable with Deedle I made up a CSV file that represents a log of video rentals.
RentedOn,Shop,Title
12/dec/2013 00:00:00,East,Rambo
12/dec/2013 00:00:00,West,Rocky
12/dec/2013 00:00:00,West,Rambo
12/dec/2013 00:00:00,East,Rambo
13/dec/2013 00:00:00,East,Rocky
13/dec/2013 00:00:00,East,Rocky
13/dec/2013 00:00:00,East,Rocky
14/dec/2013 00:00:00,West,Rocky 2
I have the following function, that groups the rentals by Shop (East or West):
let overview =
__SOURCE_DIRECTORY__ + "/rentallog.csv"
|> Frame.ReadCsv
|> Frame.groupRowsByString "Shop"
|> Frame.nest
|> Series.map (fun dtc df ->
df.GetSeries<string>("Title") |> Series.groupBy (fun k v -> v)
|> Frame.ofColumns |> Frame.countValues )
|> Frame.ofRows
I'd like to be able to filter the rows by the date in the RentedOn col, however, I'm not sure how to do this. I know its probably using the Frame.filterRowValues function but I'm unsure the best way to use this. Any guidance on how to filter would be appreciated.
Update based on #jeremyh advice
let overview rentedOnDate =
let addRentedDate (f:Frame<_,_>) =
f.AddSeries ("RentedOnDate", f.GetSeries<DateTime>("RentedOn"))
f
__SOURCE_DIRECTORY__ + "/rentallog.csv"
|> Frame.ReadCsv
|> addRentedDate
|> Frame.filterRowValues (fun row -> row.GetAs<DateTime>("RentedOnDate") = rentedOnDate)
|> Frame.groupRowsByString "Shop"
|> Frame.nest
|> Series.map (fun dtc df ->
df.GetSeries<string>("Title") |> Series.groupBy (fun k v -> v)
|> Frame.ofColumns |> Frame.countValues )
|> Frame.ofRows
Thanks,
Rob
Hey I think that you might get a faster answer if you add an f# tag to your question too.
I used the following link to answer your question which has some helpful examples.
This is the solution I came up with. Please note that I added a new column RentedOnDate that actually has a DateTime type that I do the filtering on.
let overview rentedOnDate =
let rentalLog =
__SOURCE_DIRECTORY__ + "/rentallog.csv"
|> Frame.ReadCsv
rentalLog
|> Frame.addSeries "RentedOnDate" (rentalLog.GetSeries<DateTime>("RentedOn"))
|> Frame.filterRowValues (fun row -> row.GetAs<DateTime>("RentedOnDate") = rentedOnDate)
|> Frame.groupRowsByString "Shop"
|> Frame.nest
|> Series.map (fun dtc df ->
df.GetSeries<string>("Title") |> Series.groupBy (fun k v -> v)
|> Frame.ofColumns |> Frame.countValues )
|> Frame.ofRows
// Testing
overview (DateTime.Parse "12/dec/2013 00:00:00")
How do I "convert" a Dictionary into a sequence so that I can sort by key value?
let results = new Dictionary()
results.Add("George", 10)
results.Add("Peter", 5)
results.Add("Jimmy", 9)
results.Add("John", 2)
let ranking =
results
???????
|> Seq.Sort ??????
|> Seq.iter (fun x -> (... some function ...))
A System.Collections.Dictionary<K,V> is an IEnumerable<KeyValuePair<K,V>>, and the F# Active Pattern 'KeyValue' is useful for breaking up KeyValuePair objects, so:
open System.Collections.Generic
let results = new Dictionary<string,int>()
results.Add("George", 10)
results.Add("Peter", 5)
results.Add("Jimmy", 9)
results.Add("John", 2)
results
|> Seq.sortBy (fun (KeyValue(k,v)) -> k)
|> Seq.iter (fun (KeyValue(k,v)) -> printfn "%s: %d" k v)
You may also find the dict function useful. Let F# do some type inference for you:
let results = dict ["George", 10; "Peter", 5; "Jimmy", 9; "John", 2]
> val results : System.Collections.Generic.IDictionary<string,int>
Another option, which doesn't need a lambda until the end
dict ["George", 10; "Peter", 5; "Jimmy", 9; "John", 2]
|> Seq.map (|KeyValue|)
|> Seq.sortBy fst
|> Seq.iter (fun (k,v) -> ())
with help from https://gist.github.com/theburningmonk/3363893
Given this:
[
("A","A122");
("A","A123");
("B","B122");
("B","B123");
("C","C122");
]
Is there a standard function to get this?
[
("A",["A122";"A123"]);
("B",["B122";"B123"]);
("C",["C122"])
]
I thought of Seq.distinctBy, List.partition, Set, Map, but none of them seem to be what I'm looking for.
Thanks... while I'm waiting, I'll try to roll my own :)
Silly me, I didn't notice Seq.groupBy!
[
("A","A122");
("A","A123");
("B","B122");
("B","B123");
("C","C122");
]
|> Seq.groupBy (fun (a, b) -> a)
|> Seq.map (fun (a, b) -> (a, Seq.map snd b))
Output :
seq
[("A", seq ["A122"; "A123"]); ("B", seq ["B122"; "B123"]);
("C", seq ["C122"])]
For O(1) lookup:
[
("A","A122");
("A","A123");
("B","B122");
("B","B123");
("C","C122");
]
|> Seq.groupBy fst
|> dict