F#: Fastest way to convert a seq<'A> to seq<'B> - casting

I am using Marten as an Event Store, in particular to fetch a stream of events.
type AccountCreation = {
Owner: string
AccountId: Guid
CreatedAt: DateTimeOffset
StartingBalance: decimal
}
type AccountEvents =
| AccountCreated of AccountCreation
| AccountCredited of Transaction
| AccountDebited of Transaction
let settings = {
Host = "localhost"
DatabaseName = "postgres"
UserName = "root"
Password = "root"
EventTypes = eventTypes
}
use store = createDocumentStore settings
use session = store.LightweightSession()
let khalidId = Guid.NewGuid()
let billId = Guid.NewGuid()
let khalid = AccountEvents.AccountCreated({
Owner = "Khalid Abuhakmeh"
AccountId = khalidId
StartingBalance = 1000m
CreatedAt = DateTimeOffset.UtcNow
})
let bill = {
Owner = "Bill Boga"
AccountId = billId
StartingBalance = 0m
CreatedAt = DateTimeOffset.UtcNow
}
session.Events.Append(khalidId, khalid) |> ignore
session.Events.Append(billId, bill) |> ignore
session.SaveChanges()
let stream = session.Events.FetchStream()
stream being IReadOnlyList<IEvent> and IEvent defined as:
public interface IEvent
{
Guid Id { get; set; }
int Version { get; set; }
long Sequence { get; set; }
object Data { get; }
Guid StreamId { get; set; }
string StreamKey { get; set; }
DateTimeOffset Timestamp { get; set; }
string TenantId { get; set; }
void Apply<TAggregate>(TAggregate state, IAggregator<TAggregate> aggregator) where TAggregate : class, new();
}
I would like to convert each IEvent to AccountEvents, if the underlying type of the Data property is AccountEvents (if not the item is not yielded in the resulting sequence).
In C# I would simply use the keyword as to achieve that, but in F# I am not sure what is the fastest F#-ish way(in terms of performance) to get that.
I ended up on the following code:
let seqCastOption<'T> sequence =
sequence
|> Seq.map(fun x ->
match box x with
| :? 'T as value -> Some value
| _ -> None)
let fetchStream<'T> (session: IDocumentSession) (id: Guid) =
let stream = session.Events.FetchStream(id)
stream
|> Seq.map(fun x -> x.Data)
|> seqCastOption<'T>
|> Seq.filter (fun x -> x.IsSome)
|> Seq.map(fun x -> x.Value)
But this seems quite "expensive", and I an wondering whether the step of converting .Data to the Option<AccountEvents> + filter the ones that IsSome can be done all at once.

The Seq.choose function mentioned in rmunn's answer is very useful to know for this kind of situation, but for this exact situation I would recommend using the built in .NET method Enumerable.OfType<'T>, which does exactly what you want and is probably quite optimised:
open System.Linq
let fetchStream<'T> (session: IDocumentSession) (id: Guid) =
let stream = session.Events.FetchStream(id)
stream
|> Seq.map(fun x -> x.Data)
|> Enumerable.OfType<'T>

Seq.choose is the function you've been looking for. You give it a function that takes an 'A and returns a 'B option, and it yields the 'B value of the ones that were Some. For your usage scenario, it would look like this:
let castOption<'T> x =
match box x with
| :? 'T as value -> Some value
| _ -> None
let fetchStream<'T> (session: IDocumentSession) (id: Guid) =
let stream = session.Events.FetchStream(id)
stream
|> Seq.map(fun x -> x.Data)
|> Seq.choose castOption<'T>

Related

scala code for regex pattern matching

I am new to scala. I am trying something on the regular expression pattern matching. I am following the example from here: https://alvinalexander.com/scala/how-to-extract-parts-strings-match-regular-expression-regex-scala
Given below is the code that I have written which works but is obviously not the best way.
Scenario: I have a regex pattern with me.
"([a-z0-9]+)_([0-9]+)_([v|V][0-9]+)_(\\d{4})(\\d{2})(\\d{2}).(xls|xlsx)".r
I have a string that defines what I am expecting for a given scenario. val param = "manufacturer/order/version"
Question: I don't want to pass hardcoded values in case pattern(manufacturer, order, version) but get the output in the variables manufacturer, order and version? One way is defining all the variables initially, but that would mean changing the code every time i need to change a string. Is there a way to do it dynamically or a better way of using regex in scala.
package com.testing
class DynamicFolder() {
def dynamicPath(fileName: String): Map[String, String] = {
println("File Name: " + fileName)
val param = "manufacturer/order/version"
var patternString = param.replaceAll("/", ", ")
println(patternString)
val pattern = "([a-z0-9]+)_([0-9]+)_([v|V][0-9]+)_(\\d{4})(\\d{2})(\\d{2}).(xls|xlsx)".r
val paramMap: Map[String, String] = fileName match {
case pattern(manufacturer, order, version) => {
println(s"Manufacturer: $manufacturer, Order: $order, version: $version")
Map("manufacturer" -> manufacturer, "order" -> order, "version" -> version)
}
case pattern(manufacturer, order, version, yyyy, mm, dd, format) => {
println(s"Manufacturer: $manufacturer, Order: $order, version: $version")
Map("manufacturer" -> manufacturer, "order" -> order, "version" -> version)
}
case _ => throw new IllegalArgumentException
}
paramMap
}
}
object hello {
def main(args: Array[String]): Unit = {
var dynamicFolder = new DynamicFolder
val fileName = "man1_18356_v1_20180202.xls"
val tgtParams = dynamicFolder.dynamicPath(fileName)
var tgtPath = ""
for ((k, v) <- tgtParams) {
printf("key: %s, value: %s\n", k, v)
tgtPath = tgtPath + "/" + tgtParams(k)
}
println ("Target path: "+tgtPath)
}
}
Output of the code:
File Name: man1_18356_v1_20180202.xls
manufacturer, version, order
Manufacturer: man1, Order: 18356, version: v1
key: manufacturer, value: man1
key: order, value: 18356
key: version, value: v1
Target path: /man1/18356/v1
Thanks!
This is how you can collect all groups and process them yourself:
val paramMap: Map[String, String] = fileName match {
case pattern(groups#_*) if groups.nonEmpty => {
// Access group with groups(0), groups(1) etc.
}
case _ => throw new IllegalArgumentException
}

Can't get key value as String from metadata

I'm grabbing metadata from an MPMediaItem like this:
let url = item.value(forProperty:MPMediaItemPropertyAssetURL) as? NSURL
let asset = AVURLAsset(url: url! as URL, options: nil)
let metaArray = asset.metadata
for metadata in metaArray{
print("-----metadata:\(metadata)")
print("-----metadata.key:\(String(describing: metadata.key))")
}
However, when I get a block of metadata printed the "key" is printed as a numeric value instead of "pcst" as shown in the printout:
-----metadata:<AVMetadataItem: 0x1740153f0, identifier=itsk/pcst, keySpace=itsk, key class = __NSCFNumber, key=pcst, commonKey=(null), extendedLanguageTag=(null), dataType=com.apple.metadata.datatype.int8, time={INVALID}, duration={INVALID}, startDate=(null), extras={
dataLength = 1;
dataType = 21;
dataTypeNamespace = "com.apple.itunes";
}, value=1>
-----metadata.key:Optional(1885565812)
This is happening for all of the metadata/keys (there are 29 in this particular media item).
Also note that this line of code:
let realString = NSString(string: metadata.key! as! String)
causes this error:
Could not cast value of type '__NSCFNumber' (0x1b80dcdf0) to 'NSString' (0x1b80edae8).
How can I get the string value for the key ("pcst") ?
May be what you are looking for is identifier property of AVMetadataItem.
for metadata in metaArray{
print(metadata.identifier ?? "DefaultValue")
}
In case others want to see the code I'm using:
func returnKeyString(_ inVal: String)->String{
// expecting the metadata for "identifier" as input - returns key value
// eg "itsk/ldes" -> "ldes"
// or "id3/%00WFD" etc. -> "wfd"
var sFinal:String = ""
if (inVal.contains("/")){
sFinal = (inVal.components(separatedBy: "/")[1])
}
if sFinal.contains("%"){
sFinal = sFinal.components(separatedBy: "%")[1]
let index1 = sFinal.index(sFinal.startIndex, offsetBy: 2)
sFinal = sFinal.substring(from: index1)
}
return sFinal.lowercased()
}

How to convert a dynamic list into list<Class>?

I'm trying to convert a dynamic list into a list of class-model(Products). This is how my method looks like:
public List<Products> ConvertToProducts(List<dynamic> data)
{
var sendModel = new List<Products>();
//Mapping List<dynamic> to List<Products>
sendModel = data.Select(x =>
new Products
{
Name = data.GetType().GetProperty("Name").ToString(),
Price = data.GetType().GetProperty("Price").GetValue(data, null).ToString()
}).ToList();
}
I have tried these both ways to get the property values, but it gives me null errors saying these properties doesn't exist or they are null.
Name = data.GetType().GetProperty("Name").ToString(),
Price = data.GetType().GetProperty("Price").GetValue(data,
null).ToString()
This is how my Model-class looks like:
public class Products
{
public string ID { get; set; }
public string Name { get; set; }
public string Price { get; set; }
}
Can someone please let me know what I'm missing? thanks in advance.
You're currently trying to get properties from data, which is your list - and you're ignoring x, which is the item in the list. I suspect you want:
var sendModel = data
.Select(x => new Products { Name = x.Name, Price = x.Price })
.ToList();
You may want to call ToString() on the results of the properties, but it's not clear what's in the original data.

Get object of case class from regex match

i'm working on scraping data from a webpage with scala regex-es, but i encountered problem with parsing result to object of some case class-es.
In following snippet i managed to scrape all the data, but i have no clue how to parse 3 elements from an iterator. I thought about something like:
val a :: b :: c :: _ = result.group(0).iDontKnowWha
Any ideas what can i do?
import model.FuneralSchedule
import play.api.libs.json.Json
import scala.io.Source
var date = "2015-05-05"
val source = Source.fromURL("http://zck.krakow.pl/?pageId=16&date=" + date).mkString
val regex = "(?s)<table>.+?(Cmentarz.+?)<.+?</table>".r
var thing: List[FuneralSchedule] = List()
var jsonFeed: List[Funeral] = List()
val regMatcher = "("
case class Funeral(hour: String, who: String, age: String) {
override def toString: String = {
"Cos"
}
}
//implicit val format = Json.format[Funeral]
val out = regex.findAllIn(source).matchData foreach { table =>
thing ::= FuneralSchedule(table.group(1), clearStrings(table.group(0)))
"""<tr\s?>.+?</\s?tr>""".r.findAllIn(clearStrings(table.group(0))).matchData foreach { tr =>
//TODO: Naprawic bo szlak trafia wydajnosc
val temp = """<td\s?>.+?</\s?td>""".r.findAllIn(tr.group(0)).matchData.foreach {
elem => println(elem)
}
//println(Json.toJson(thingy))
}
println("Koniec tabeli")
}
thing
//Json.toJson(jsonFeed)
println(removeMarkers("<td > <td> Marian Debil </ td>"))
def removeMarkers(s: String) = {
s.replaceAll( """(</?\s?td\s?>)""", "")
}
def clearStrings(s: String) = {
val regex = "((class=\".+?\")|(id=\".+?\")|(style=\".+?\")|(\\n))"
s.replaceAll(regex, "")
}
One way of doing it would be converting it to a Stream and matching it using stream's operators like this:
val a #:: b #:: c #:: _ = """([a-z]){1}""".r.findAllIn("a b c").toStream
then a, b and c is what you're looking for

Convert Set-Cookie string to record in F# way?

I have the following record of Cookie.
type Cookie = {
NameValue : string * string;
Domain : string option;
Path : string option;
Expires : string option;
MaxAge : string;
Secure : bool; // ? no associated value, anything better than bool
HttpOnly : bool; // ? no associated value, anything better than bool
}
And I need to convert a set-cookie string to CookieAttricute list.
let GetCookie str =
let (|ParseRegex|_|) regex str =
let m = Regex(regex, RegexOptions.IgnoreCase).Match(str)
if m.Success
then Some (List.tail [ for x in m.Groups -> x.Value ])
else None
match str with
| ParseRegex #"(.+?)(?:=(.+?))?(?:;|$|,(?!\s))" [name; value] ->
.... // How to construct a Cookie here?
| _ -> None
For example, given the following string.
"ObSSOCookie=93KRPo;secure; httponly; path=/; domain=.xyz.com"
The function should return
{ NameValue = ("ObSSOCookie", "93KRPo");
Secure = true;
HttpOnly = true;
Path = Some("/");
Domain = Some(".xyz.com");
MaxAge = None;
Expires = None }
First, I would use String.Split instead of overcomplicating it with regexes. Then, I would build a Map<string, string> for the attributes. Something like this:
let GetCookie (str: string) =
let allkeyvalues =
str.Split ';'
|> Array.map (fun str ->
match str.Split '=' with
| [| key |] -> (key, "")
| [| key; value |] -> (key, value)
| _ -> (* there's more than one '='; the format is incorrect. *))
let namevalue = allkeyvalues.[0]
let attributes =
Seq.skip 1 allkeyvalues
|> Seq.map (fun (key, value) ->
(key.ToLower(), value)) // attribute names are case-insensitive
|> Map.ofSeq
{
NameValue = namevalue
Secure = Map.containsKey "secure" attributes
Path = Map.tryFind "path" attributes
// etc...
}