I have a data contract (WCF) with a field defined as:
[<DataContract(Namespace = _Namespace.ws)>]
type CommitRequest =
{
// Excluded for brevity
...
[<field: DataMember(Name="ExcludeList", IsRequired=false) >]
ExcludeList : int array option
}
I want to from the entries in the ExcludeList, create a comma separated string (to reduce the number of network hops to the database to update the status). I have tried the following 2 approaches, neither of which create the desired string, both are empty:
// Logic to determine if we need to execute this block works correctly
try
// Use F# concat
let strList = request.ExcludeList.Value |> Array.map string
let idString = String.concat ",", strList
// Next try using .NET Join
let idList = String.Join ((",", (request.ExcludeList.Value.Select (fun f -> f)).Distinct).ToString ())
with | ex ->
...
Both compile and execute but neither give me anything in the string. Would greatly appreciate someone pointing out what I am doing wrong here.
let intoArray : int array option = Some [| 1; 23; 16 |]
let strList = intoArray.Value |> Array.map string
let idString = String.concat "," strList // don't need comma between params
// Next try using .NET Join
let idList = System.String.Join (",", strList) // that also works
Output:
>
val intoArray : int array option = Some [|1; 23; 16|]
val strList : string [] = [|"1"; "23"; "16"|]
val idString : string = "1,23,16"
val idList : string = "1,23,16"
Related
I got this problem. I have a
val line:String = "PE018201804527901"
that matches with this
regex : (.{2})(.{4})(.{9})(.{2})
I need to extract each group from the regex to an Array.
The result would be:
Array["PE", "0182","018045279","01"]
I try to do this regex:
val regex = """(.{2})(.{4})(.{9})(.{2})""".r
val x= regex.findAllIn(line).toArray
but it doesn't work!
regex.findAllIn(line).subgroups.toArray
Note that findAllIn does not automatically anchor the regex pattern, and will find a match inside a much longer string. If you need to only allow matches inside 17 char strings, you can use a match block like this:
val line = "PE018201804527901"
val regex = """(.{2})(.{4})(.{9})(.{2})""".r
val results = line match {
case regex(g1, g2, g3, g4) => Array(g1, g2, g3, g4)
case _ => Array[String]()
}
// Demo printing
results.foreach { m =>
println(m)
}
// PE
// 0182
// 018045279
// 01
See a Scala demo.
It also handles no match scenario well initializing an empty string array.
If you need to get all matches and all groups, then you will need to grab the groups into a list and then add the list to a list buffer (scala.collection.mutable.ListBuffer):
val line = "PE018201804527901%E018201804527901"
val regex = """(.{2})(.{4})(.{9})(.{2})""".r
val results = ListBuffer[List[String]]()
val mi = regex.findAllIn(line)
while (mi.hasNext) {
val d = mi.next
results += List(mi.group(1), mi.group(2), mi.group(3), mi.group(4))
}
// Demo printing
results.foreach { m =>
println("------")
println(m)
m.foreach { l => println(l) }
}
Results:
------
List(PE, 0182, 018045279, 01)
PE
0182
018045279
01
------
List(%E, 0182, 018045279, 01)
%E
0182
018045279
01
See this Scala demo
Your solution #sheunis was very helpful, finally I resolved it with this method:
def extractFromRegex (regex: Regex, line:String): Array[String] = {
val list = ListBuffer[String]()
for(m <- regex.findAllIn(line).matchData;
e <- m.subgroups)
list+=e
list.toArray
}
Because your solution with this code:
val line:String = """PE0182"""
val regex ="""(.{2})(.{4})""".r
val t = regex.findAllIn(line).subgroups.toArray
Shows the next exception:
Exception in thread "main" java.lang.IllegalStateException: No match available
at java.util.regex.Matcher.start(Matcher.java:372)
at scala.util.matching.Regex$MatchIterator.start(Regex.scala:696)
at scala.util.matching.Regex$MatchData$class.group(Regex.scala:549)
at scala.util.matching.Regex$MatchIterator.group(Regex.scala:671)
at scala.util.matching.Regex$MatchData$$anonfun$subgroups$1.apply(Regex.scala:553)
at scala.util.matching.Regex$MatchData$$anonfun$subgroups$1.apply(Regex.scala:553)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
at scala.collection.immutable.List.foreach(List.scala:318)
at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
at scala.collection.AbstractTraversable.map(Traversable.scala:105)
at scala.util.matching.Regex$MatchData$class.subgroups(Regex.scala:553)
at scala.util.matching.Regex$MatchIterator.subgroups(Regex.scala:671)
I have an array of Strings containing user inputted values. I have a string containing several words (the number of words in the string varies). I want to increment an Int every time one of the words in the string matches a word in the array.
I'm using this method:
var searchWordsArr: [String] = [] \\filled by user input
var message: String = "" \\random number of words
var numRelevantWords = 0
var i = 0
while i < self.searchWordsArr.count {
i+=1
if message.contains(self.searchWordsArr[i-1]) {
numRelevantWords += 1
}
}
In my first example, the string contained 25 words and the array contained 3 words. The 3 words came up a total of 12 times in the string. Using the above method, the value of numRelevantWords was 2.
I would use regex. Some day soon, Swift will have native regular expressions. But until it does, you have to resort to Foundation:
let words = ["the", "cat", "sat"]
let input = "The cat sat on the mat and the mat sat on the catastrophe"
var result = 0
let pattern = "\\b(" + words.joined(separator:"|") + ")\\b"
do {
let regex = try NSRegularExpression(pattern: pattern, options: .caseInsensitive)
let match = regex.matches(in: input, options: [], range: NSRange(location: 0, length: input.utf16.count))
result += match.count // 7, that's your answer
} catch {
print("illegal regex")
}
Add them to a NSCountedSet:
let searchWords = NSCountedSet()
searchWords.add("Bob")
searchWords.add("John")
searchWords.add("Bob")
print(searchWords.count(for: "Bob")) // 2
"pure" Swift (no Foundation)
let message = "aa bb aaaa ab ac aa aa"
let words = message.characters.split(separator: " ").map(String.init)
let search:Set = ["aa", "bb", "aa"]
let found = words.reduce(0) { (i, word) -> Int in
var i = i
i += search.contains(word) ? 1 : 0
return i
}
print(found, "word(s) from the message are in the search set")
prints
4 word(s) from the the message are in the search set
UPDATE (see discussion)
using Set
var search: Set<String> = [] // an empty set with Element == String
search.insert("aa") // insert word to set
using Array
var search: Array<String> = [] // an empty array
search.append("aa") // append word to array
maybe you are looking for
let message = "the cat in the hat"
let words = message.characters.split(separator: " ").map(String.init)
let search:Set = ["aa", "bb", "pppp", "the"]
let found = search.reduce(0) { (i, word) -> Int in
var i = i
i += words.contains(word) ? 1 : 0
return i
}
print(found, "word(s) from the search set found in the message")
prints
1 word(s) from the search set found in the message
if you would like to produce the same as with accepted answer
let words = ["the", "cat", "sat"]
let input = "The cat sat on the mat and the mat sat on the catastrophe"
let inputWords = input.lowercased().characters.split(separator: " ").map(String.init)
let found = inputWords.reduce(0) { (i, word) -> Int in
var i = i
i += words.contains(word.lowercased()) ? 1 : 0
return i
}
print(found, "word(s) from the search set found in the message")
prints
7 word(s) from the search set found in the message
I am trying to dynamically chop an array of letters but I can't seem to reconvert the result back into a [String]
let letters:String = "abcdefghijklmnopqrstuvwxyz"
let lettersarray = Array(letters.characters)
var targetNum = 14 // Show just the first 14 characters
var resultsArray = [String]()
let resultsSlice = lettersarray.dropLast(lettersarray.count - targetNum) // Returns an Array Slice instead of an Array
let newresultsArray = Array(resultsSlice) // Returns Array<_element> instead of [String]
How do I return a [String] ie ["a","b","c"... eg]
You need to map the Character array back to String
let resultsArray = lettersarray.dropLast(lettersarray.count - targetNum).map{String($0)}
alternatively (credits to Leo Dabus)
let letters = "abcdefghijklmnopqrstuvwxyz"
let targetNum = 14
let resultsArray = letters.characters.prefix(targetNum).map{String($0)}
No need for an array here. It's hard to understand what you're trying to do, but if you just want the first 14 characters of a string, use the prefix method:
let s = String("abcdefghijklmno".characters.prefix(14))
Assuming that you are getting a String and want an array of characters which is a slice of characters from that String, you could use Swift's Half-Open Range Operator found here.
let letters:String = "abcdefghijklmnopqrstuvwxyz"
let lettersArray = Array(letters.characters)
let targetNum = 2
let resultsArray = lettersArray[0..<targetNum]
This will give you an ArraySlice<Character>. If you want an Array<Character> you could do this:
let resultsArray:Array<Character> = Array(lettersArray[0..<targetNum]) // ["a","b"]
I am new to programming and F# is my first language.
Here is part of my code:
let splitArrayIntoGroups (inputArray: string[]) (groupSize: int) =
let groups = new LinkedList<string[]>()
let rec splitRecursively currentStartIndex currentEndIndex =
groups.AddLast(inputArray.[currentStartIndex..currentEndIndex]) |> ignore
let newEndIndex = Math.Min((inputArray.Length - 1), (currentEndIndex + groupSize))
if newEndIndex <> currentEndIndex then
splitRecursively (currentStartIndex + groupSize) newEndIndex
splitRecursively 0 (groupSize - 1)
groups
I want this function to be able to accept arrays of any type (including types that I define myself) as input. What changes should I make?
This was already answered but here you have an implementation not using a linked list but just an array of lists
let rec split<'T> (input: 'T array) size =
let rec loopOn (tail : 'T array) grouped =
let lastIndex = Array.length tail - 1
let endindx = min (size - 1) lastIndex
let arrWrapper = (fun e -> [|e|])
let newGroup = tail.[0..endindx]
|> List.ofArray
|> arrWrapper
|> Array.append grouped
match tail with
| [||] -> newGroup
|> Array.filter (fun e -> List.length e > 0)
| _ -> loopOn tail.[endindx + 1..] newGroup
let initialState = [|List.empty<'T>|]
loopOn input initialState
Because this is generic implementation you can call it with different types
type Custom = {Value : int}
let r = split<int> [|1..1000|] 10
let r2 = split<float> [|1.0..1000.0|] 10
let r3 = split<Custom> [|for i in 1..1000 ->
{Value = i}|] 10
replace string[] with _[] in the function signature.
I'm trying to fold an array of arrays of strings into a single string but I'm not having much luck. Unfortunately it seems Array.reduce expects my lambda to return an array of strings because it is an array of array of strings.
I'm getting :
Line error 37: The type 'string[]' does not match the type 'string'
This is the offending line
(fold state) + (fold item)
Because it's expecting the lambda to return a string[]
Here is the code:
let splitStr (seperator: string[]) (str: string) = str.Split(seperator, StringSplitOptions.None)
let convertFile fileName =
let arrayToTransaction arr =
let rec foldArray index (sb: StringBuilder) (arr:string[]) =
if index > 5 then sb.ToString()
else
let text =
match index with
| 0 -> sb.Append(DateTime.Parse(arr.[1]).ToString("dd/MM/yy", CultureInfo.InvariantCulture))
| 1 -> sb.Append(arr.[0].Substring(0, arr.[0].IndexOf(',')).Trim())
| 2 -> sb.Append("Test")
| 3 -> sb.Append("Test")
| 4 -> sb.Append(Single.Parse(arr.[2].Substring(arr.[2].IndexOf('-') + 1)).ToString("F2", CultureInfo.InvariantCulture))
| _ -> sb.Append(String.Empty)
foldArray (index + 1) (text.Append(",")) arr
arr
|> Array.map (splitStr [|"\n"|])
|> Array.reduce (fun state item -> let fold x = foldArray 0 (new StringBuilder()) x
(fold state) + (fold item))
File.ReadAllText(fileName)
|> splitStr [|"\r\n\r\n"|]
|> arrayToTransaction
Your lambda in the Array.reduce must return a string[] since the signature of the lambda is 'T->'T->'T and the first 'T is already unified as string[] so the result should also be a string[]