I have a string in F#:
let name = "Someone"
I also have an array of strings:
let mutable arraysOfNames : string[] = [||]
I want to add the string name to the array arrayOfNames. How do I do that? It doesn't have to be an array, it can be a Sequence or any other collection that I can check then if it is empty or not.
It is not possible to add an item to a zero-length array. All you can do is create a new array that holds the item. The currently accepted answer, using Array.append, does not modify the input arrays; rather, it creates a new array that contains the elements of both input arrays. If you repeatedly call append to add a single element to an array, you will be using memory extremely inefficiently.
Instead, in f#, it makes much more sense to use F#'s list type, or, for some applications, ResizeArray<'T> (which is f#'s name for the standard .NET List). If you actually need the items in an array (for example, because you have to pass them to a method whose parameter is an array type), then do as Steve suggests in his comment. Once you have built up the collection, call List.toArray or Array.ofList if you're using an F# list, or call the ToArray() instance method if you're using a ResizeArray.
Example 1:
let mutable listOfNames : string list = []
listOfNames <- "Alice" :: listOfNames
listOfNames <- "Bob" :: listOfNames
//...
let names = listOfNames |> List.toArray
Example 2:
let resizeArrayOfNames = ResizeArray<string>()
resizeArrayOfNames.Add "Alice"
resizeArrayOfNames.Add "Bob"
let names = resizeArrayOfNames.ToArray()
Note that in the first example, you'll get the names in reverse order; if you need them in the same order in which they were added, you'd need
let names = listOfNames |> List.rev |> List.toArray
For any Seq which is IEnumerable<T> alias in F# you can write this function:
let addToSequence aseq elm = Seq.append aseq <| Seq.singleton elm
And use it this way:
let withSomeone = addToSequence [||] "Someone"
You can use Seq.toArray or Seq.toList after you get a result sequence
Take a look at Array.append.
// Signature: Array.append : 'T [] -> 'T [] -> 'T []
// Usage: Array.append array1 array2
So in your case, you can use it like this:
let name = "someone"
let mutable arrayOfNames = Array.append [|"test"; "abc"|] [|name|]
printfn "%A" arrayOfNames
//[|"test"; "abc"; "someone"|]
So you simply need to transform your string into an array (by using [|string|]). Since Array contains the append function, you can append a string this way to an array.
You can use Array.append with mutable arrays:
let mutable my_array = [||]
for i in 0 .. array_size do
my_array <- [|i|] |> Array.append my_array
// my_array <- Array.append my_array [|i|]
printfn "my array is: %A" my_array
Related
I'm a newbie at F#,
I've got a List that contains arrays, each arrays contains 7 Strings.
I want to loop through the Arrays and do some kind of Array.map later on,
However my problem is that I can't send individual arrays to some other function.
I don't want to use for-loops but focus on the functional way using pipelines and mapping only.
let stockArray =
[[|"2012-03-30"; "32.40"; "32.41"; "32.04"; "32.26"; "31749400"; "32.26"|];
[|"2012-03-29"; "32.06"; "32.19"; "31.81"; "32.12"; "37038500"; "32.12"|];
[|"2012-03-28"; "32.52"; "32.70"; "32.04"; "32.19"; "41344800"; "32.19"|];
[|"2012-03-27"; "32.65"; "32.70"; "32.40"; "32.52"; "36274900"; "32.52"|];]
let tryout =
stockArray
|> List.iter;;
Output complains about List.iter:
error FS0001: Type mismatch. Expecting a
'string [] list -> 'a' but given a
'('b -> unit) -> 'b list -> unit'
The type 'string [] list' does not match the type ''a -> unit'
When trying Array.iter, same difference:
error FS0001: Type mismatch. Expecting a
'string [] list -> 'a' but given a
'('b -> unit) -> 'b [] -> unit'
The type 'string [] list' does not match the type ''a -> unit'
In C# I would simply go about it with a foreach to start treating my arrays one at a time, but with F# I feel real stuck.
Thank you for your help
The question is not clear, even with the extra comments. Anyway, I think you will finally be able to figure out your needs from this answer.
I have implemented parseDate and parseFloat in such a way that I expect it to work on any machine, whatever locale, with the given data. You may want something else for your production application. Also, how theInt is calculated is perhaps not what you want.
List.iter, as you already discovered, converts data to unit, effectively throwing away data. So what's the point in that? It is usually placed last when used in a pipe sequence, often doing some work that involves side effects (e.g. printing out data) or mutable data operations (e.g. filling a mutable list with items). I suggest you study functions in the List, Array, Seq and Option modules, to see how they're used to transform data.
open System
open System.Globalization
let stockArray =
[
[| "2012-03-30"; "32.40"; "32.41"; "32.04"; "32.26"; "31749400"; "32.26" |]
[| "2012-03-29"; "32.06"; "32.19"; "31.81"; "32.12"; "37038500"; "32.12" |]
[| "2012-03-28"; "32.52"; "32.70"; "32.04"; "32.19"; "41344800"; "32.19" |]
[| "2012-03-27"; "32.65"; "32.70"; "32.40"; "32.52"; "36274900"; "32.52" |]
]
type OutData = { TheDate: DateTime; TheInt: int }
let parseDate s = DateTime.ParseExact (s, "yyyy-MM-dd", CultureInfo.InvariantCulture)
let parseFloat s = Double.Parse (s, CultureInfo.InvariantCulture)
let myFirstMap (inArray: string[]) : OutData =
if inArray.Length <> 7 then
failwith "Expected array with seven strings."
else
let theDate = parseDate inArray.[0]
let f2 = parseFloat inArray.[2]
let f3 = parseFloat inArray.[3]
let f = f2 - f3
let theInt = int f
{ TheDate = theDate; TheInt = theInt }
let tryout =
stockArray
|> List.map myFirstMap
The following is an alternative implementation of myFirstMap. I guess some would say it's more idiomatic, but I would just say that what you prefer to use depends on what you might expect from a possible future development.
let myFirstMap inArray =
match inArray with
| [| sDate; _; s2; s3; _; _; _ |] ->
let theDate = parseDate sDate
let f2 = parseFloat s2
let f3 = parseFloat s3
let f = f2 - f3
let theInt = int f
{ TheDate = theDate; TheInt = theInt }
| _ -> failwith "Expected array with seven strings."
The pipe operator |> is used to write an f x as x |> f.
The signature of List.iter is:
action: ('a -> unit) -> list: ('a list) -> unit
You give it an action, then a list, and it gives you a void.
You can read it thus: when you give List.iter an action, its type will be
list: ('a list) -> unit
a function to which you can pass a list.
So when you write stockArray |> List.iter, what you're actually trying to give it in place of an action is your list - that's the error. So pass in an action:
let tryout = List.iter (fun arr -> printfn "%A" arr) stockArray
which can be rewritten as:
let tryout = stockArray |> List.iter (fun arr -> printfn "%A" arr)
However my problem is that I can't send individual arrays to some other function
List.map and similar functions allow you to do precisely this - you don't need to iterate the list yourself.
For example, this will return just the first element of each array in your list:
stockArray
|> List.map (fun x -> x.[0])
You can replace the function passed to List.map with any function that operates on one array and returns some value.
I have an Array of Image links -
let alamofireSource = [AlamofireSource(urlString: Img1!)!, AlamofireSource(urlString: Img2!)!,
AlamofireSource(urlString: Img3!)!, AlamofireSource(urlString: Img4!)!]
slideshow.setImageInputs(alamofireSource)
some posts have only one image or two or three, and so on. so, sometimes image 2 (for example) is nil, In that case, I don't want it to be added to the array, is that possible?
You can try ( Swift 4 )
let arr = [img1,img2].compactMap{$0}.map{AlamofireSource(urlString:$0)!}
or
let arr = alamofireSource.compactMap{$0}
for Swift 3
let arr = alamofireSource.flatMap{$0}
so, sometimes image 2 (for example) is nil, In that case, I don't want
it to be added to the array, is that possible?
Yes it is. Although I would go with Sh_Khan's suggestion to use the compactMap method to achieve it, but it would be useless for your current case:
Based on your code snippet, I'd assume that alamofireSource of type [AlamofireSource], but not [AlamofireSource?] and that's because you are forcibly unwrap its elements (by adding ! to each of its elements). So far alamofireSource doesn't contain nils (actually it could be more danger than just a declaration, your app might crash!)
So first of all, I would recommend to remove the ! from alamofireSource:
let alamofireSource = [AlamofireSource(urlString: Img1!),
AlamofireSource(urlString: Img2!),
AlamofireSource(urlString: Img3!),
AlamofireSource(urlString: Img4!)]
which means let it be as [AlamofireSource?], therefore you would gain the benefit of using compactMap(_:):
Returns an array containing the non-nil results of calling the given
transformation with each element of this sequence.
As:
let alamofireSourceWihoutNils = alamofireSource.compactMap { $0 }
Assuming you put your Optional url strings into an array, say urlStrings (of type [String?]), you can construct alamofireSource according to (Swift 4):
let alamofireSource = urlStrings.compactMap { $0.map(AlamofireSource.init) }
Which make use of the map(_:) operator of Optional and compactMap(_:) to unwrap the two-level optionality.
Details
Your example contains two levels of optionality:
The optional ImgX arguments of type String? - henceforth referred to and named as img1, ..., img4, as CapitalFirstLetter names are reserved for e.g. types, not type instances.
The failable initilizer init?(urlString: String, placeholder: UIImage? = nil) of AlamofireSource.
First of all, lets gather the optional image links (imgX) into an array
let urlStrings = [url1, url2, url3, url4] // [String?]
Swift 4
You can combine the map(_:) operator of Optional with compactMap(_:) to safely unwrap and make use of the .some entires of urlStrings, thereafter collect the successful invocations of the failable initializer of AlamofireSource:
let alamofireSource = urlStrings.compactMap { $0.map(AlamofireSource.init) }
// or, use a named closure argument
let alamofireSource = urlStrings.compactMap { str in str.map(AlamofireSource.init) }
Swift 3
If using Swift 3, replace the compactMap(_:) invocation above with flatMap(_:):
let alamofireSource = urlStrings.flatMap { $0.map(AlamofireSource.init) }
// or, use a named closure argument
let alamofireSource = urlStrings.flatMap { str in str.map(AlamofireSource.init) }
I'm having trouble adding an existing array to the beginning of another array.
For example:
MutableID array contains 1,2,3,4,5 & idArray array contains 6,7,8
self.MutableID.addObjectsFromArray(idArray as [AnyObject])
//currently puts values to the end of the array not the beginning
this code outputs 1,2,3,4,5,6,7,8 but I want it to output 6,7,8,1,2,3,4,5
I need the values added to the beginning of self.MutableID Any suggestions on how I can accomplish this?
NSMutableArray has insertObjects method.
self.MutableID.insertObjects(otherArray as [AnyObject], atIndexes: NSIndexSet(indexesInRange: NSMakeRange(0, otherArray.count)))
Or you can assign the mutable array to a swift array and use insertContentsOf method ;)
self.MutableID = NSMutableArray(array: [1,2,3,4])
var otherArray = NSMutableArray(array: [6,7,8,9])
var swiftArr : [AnyObject] = self.MutableID as [AnyObject]
swiftArr.insertContentsOf(otherArray as [AnyObject], at: 0)
self.MutableID.insertContentsOf(idArray as [AnyObject], at: 0)
This question is the Swift version of this question which solves the problem in Objective-C.
If we must, for whatever reason, be using Objective-C's NSArray or NSMutableArray, then we can simply use a Swift translation of the code in my answer over there:
let range = NSMakeRange(0, newArray.count)
let indexes = NSIndexSet(indexesInRange: range)
oldArray.insertObjects(newArray as [AnyObject], atIndexes: indexes)
Where oldArray is an NSMutableArray and newArray is NSArray or NSMutableArray.
Or the other approach, append and reassign:
oldArray = newArray.arrayByAddingObjectsFromArray(oldArray as [AnyObject])
But the most correct thing to do would be to use the Swift Array type, and then use the insertContentsOf(_: Array, at: Int) method, as fluidsonic's answer describes.
I'm using Array(0, {i -> ""}) currently, and I would like to know if there's a better implementation such as Array()
plus, if I'm using arrayOfNulls<String>(0) as Array<String>, the compiler will alert me that this cast can never succeed. But it's the default implementation inside Array(0, {i -> ""}). Do I miss something?
As of late (June 2015) there is the Kotlin standard library function
public fun <T> arrayOf(vararg t: T): Array<T>
So to create an empty array of Strings you can write
val emptyStringArray = arrayOf<String>()
Just for reference, there is also emptyArray. For example,
var arr = emptyArray<String>()
See
doc
Array.kt
Empty or null? That's the question!
To create an array of nulls, simply use arrayOfNulls<Type>(length).
But to generate an EMPTY array of size length, use:
val arr = Array(length) { emptyObject }
Note that you must define an emptyObject properly per each data-type (beacause you don't want nulls). E. g. for Strings, emptyObject can be "". So:
val arr = Array(3) { "" } // is equivalent for: arrayOf("","","")
Here is a live example. Note that the program runs with two sample arguments, by default.
null array
var arrayString=Array<String?>(5){null}
var nullArray= arrayOfNulls<String>(5)
As mentioned above, you can use IntArray(size) or FloatArray(size).
I found two ways to create an empty array, the second way without a lambda:
var arr = Array (0, { i -> "" })
var arr2 = array<String>()
Regarding Kotlin's null strings, this is not allowed. You have to use String? type to allow strings to be null.
Use:
#JvmField val EMPTY_STRING_ARRAY = arrayOfNulls<String>(0)
It returns an 0 size array of Strings, initialized with null values.
1. Wrong:
#JvmField val EMPTY_STRING_ARRAY = emptyArray<String>()
It returns arrayOfNulls<String>(0)
2. Wrong:
#JvmField val EMPTY_STRING_ARRAY = arrayOf<String>()
It returns an array containing the Strings.
Simplest way to initialise array and assigning values :
val myArray: Array<String> = Array(2) { "" }
myArray[0] = "Java"
myArray[1] = "Kotlin"
I've been stuck for a while with this seemingly basic problem. I have a two dimensional array of strings and another one dimensional array of strings. The one dimensional array consists of some of the elements present in one of the columns of the two dimensional array. The result that I wish to get is a two dimensional array which is filtered by the elements in the two-dimensional array. As an example:
two-dimensional array:
[["A", "elephant"], ["B", "dog"] , ["C", "cat"] , ["D", "mouse"], ["E", "giraffe"]]
one-dimensional array:
["elephant" , "cat" , "giraffe"]
desired result:
[["A", "elephant] , ["C", "cat"] , ["E", "giraffe"]]
I thank you in advance for your help. I'm pretty new to F# and trying to learn it has been difficult until now.
cheers
Let's say you have a list of tuples like this one:
let animalList =
[("A", "elephant"); ("B", "dog"); ("C", "cat"); ("D", "mouse"); ("E", "giraffe")]
And another list of animals you want to keep, let's make it a set while we're at it:
let animalsToKeep =
["elephant"; "cat"; "giraffe"] |> Set.ofList
Then define a function that filters a list of tuples, keeping only those that appear in a given set
let filterWithSet set lst =
lst
|> List.filter (fun (_, elem) -> Set.contains elem set)
And call it:
filterWithSet animalsToKeep animalList
The answer depends on what you actually want to do, but it sounds like finding the right representation is the most important part of the question. In your example, your nested lists always contain just two values (e.g. "A" and "elephant") and so it would make more sense to use a list of tuples:
let things = [ ("A", "elephant"); ("B", "dog"); ("C", "cat");
("D", "mouse"); ("E", "giraffe")]
This representation will make things easier, because we only need to check if the second element of the tuple is in the list used for filtering:
let filter = ["elephant" ; "cat" ; "giraffe"]
To do that, you can use List.filter to filter a list. In the condition, you can get the animal using snd (get second element of a tuple) and then use List.exist to see if it is in the list of animals to be included:
things |> List.filter (fun nested ->
let animal = snd nested
filter |> List.exists (fun a -> a = animal))
If you want to make the lookup more efficient, you can create a set of filtered items:
let filter = set ["elephant" ; "cat" ; "giraffe"]
things |> Seq.filter (fun nested -> filter.Contains(snd nested))
And, in fact, you can use function composition to call snd followed by the check:
things |> Seq.filter (snd >> filter.Contains)
This means exactly the same thing as the line above - it takes the tuple with the letter and animal name, extracts the animal name using the snd function and then passes the name to filter.Contains to see if it is in the set.