I just learn Crystal, but stuck on an array of mix type hashes.
Suppose there is an array of hashes:
array = [{"a" => "Aa", "b" => 66, "c" => Time.now}]
I can easily add another element:
array << {"c" => "Bb", "d" => 2, "f" => 1.year.from_now}
But when I start from the empty array:
empty = [] of Hash(String, String | Time | Int64)
and try to add new element, an error appears:
empty << {"a" => "Aa", "b" => 66, "c" => Time.now}
# no overload matches 'Array(Hash(String, Int64 | String | Time))#<<'
# with type Hash(String, Int32 | String | Time)
Could you please explain what I do wrong?
Link to sample code
You have a little mistake. You defined hash with String, Int64 | String | Time but trying to add hash with String, Int32 | String | Time.
Just change Int64 to Int32 and this will be work, see fixed example — https://play.crystal-lang.org/#/r/6185
Related
Is there some function or syntax construction to make next examples work?
Invoke Hash#values_at function with an Array argument:
h = {"a" => 1, "b" => 2, "c" => 3}
ary = ["a", "b"]
h.values_at(*ary) # Error: argument to splat must be a tuple, not Array(String)
Pass Hash to initialize class or struct:
struct Point
def initialize(#x : Int32, #y : Int32)
end
end
h = {"x" => 1, "y" => 2}
Point.new(**h) # Error: argument to double splat must be a named tuple, not Hash(String, Int32)
The first example might be impossible, depending on the circumstances. However if the length of elements is fixed, you can do:
h = {"a" => 1, "b" => 2, "c" => 3}
ary = ["a", "b"]
p h.values_at(*{String, String}.from(ary))
https://carc.in/#/r/3oot See Tuple.from
NamedTuple supports the same approach:
struct Point
def initialize(#x : Int32, #y : Int32)
end
end
h = {"x" => 1, "y" => 2}
p Point.new(**{x: Int32, y: Int32}.from(h))
https://carc.in/#/r/3oov See NamedTuple.from
Both of these are just some sugar around ensuring types and decomposing the structure manually at runtime and mainly useful when your data comes from an external source, such as being parsed from JSON.
Of course it's preferred to create and use Tuple over Array and NamedTuple over Hash in the first place where possible.
Array and Hash are dynamically expanding containers, the number of elements can change at runtime and maybe the array might be empty when you try to splat it.
Tuple and NamedTuple consist of a fixed number of elements which is known at compile time so they can be used for splats. If the format of your data containers does not change, you can just use Tuple and NamedTuple instead.
ary = {"a", "b"}
h.values_at(*ary)
I'm new to f# and fsUnit and I'm wondering how to test a pattern matching statement using fsUnit. For example, if i have the following code how would you write a fsunit test for it?
let Menu () =
let Choice = Console.ReadLine()
match Choice with
| "A" | "a" -> Function1()
| "B" | "b" -> Function2()
| "C" | "c" -> Function3()
| _ -> Printfn"Error"
First of all, you'd separate the code that implements the matching logic from the code that reads the input, because you can only test that the result of some call is correct:
let handleInput choice =
match choice with
| "A" | "a" -> Function1()
| "B" | "b" -> Function2()
| "C" | "c" -> Function3()
| _ -> "Error"
let menu () =
let choice = Console.ReadLine()
let output = handleInput choice
printfn "%s" output
Now you can write a series of tests that check that the string returned by handleInput is the string that you are expecting for each input:
handleInput "A" |> should equal "whatever Function 1 returns"
handleInput "b" |> should equal "whatever Function 2 returns"
handleInput "D" |> should equal "Error"
Given:
iex(9)> list_of_maps = [%{"a" => 1, "b" => 2, "c" => 3},%{"a" => 66, "b" => 1, "c" => 9},%{"a" => 66, "b" => 20, "c" => 8}]
I can do:
iex(10)> Enum.filter(list_of_maps, &(&1["a"] == 1))
Enum.filter(list_of_maps, &(&1["a"] == 1))
[%{"a" => 1, "b" => 2, "c" => 3}]
However now comes the part I dread writing in every language - getting the first value of this list to extract the single item.
Is there some standard function in elixir that filters a list, returning single item if there is only one item after a filter is applied, or a list of items if there are numerous items returned after the filter is applied? Like:
iex(11)> Enum.filterr(list_of_maps, &(&1["a"] == 1))
Enum.filter(list_of_maps, &(&1["a"] == 1))
%{"a" => 1, "b" => 2, "c" => 3}
iex(12)> Enum.filterr(list_of_maps, &(&1["a"] == 66))
Enum.filter(list_of_maps, &(&1["a"] == 66))
[%{"a" => 66, "b" => 1, "c" => 9},%{"a" => 66, "b" => 20, "c" => 8}]]
Find one item from a List
If you want to filter a list to get only one item, use Enum.find/2:
Enum.find(list_of_maps, fn m -> m["a"] == 1 end)
Get one or a list of matches
To handle both cases, pattern matching is the way to go:
defmodule MyEnum do
def filter(list, fun) do
list
|> Enum.filter(fun)
|> normalize
end
defp normalize([item]), do: item
defp normalize(list), do: list
end
You can then use it like this:
MyEnum.filter(list_of_maps, &(&1["a"] == 1))
It will return a list if there are multiple matches or the map itself if there's only one match.
Just pipe your filtered list into Enum.at/2
first = list_of_maps |> Enum.filter(&(&1["a"] == 66)) |> Enum.at(0)
That will get the first element of the filtered list regardless of size.
Also, as Dogbert mentioned in the comments you can just use Enum.find/2 instead of filter to just find the first match.
Enum.find(list_of_maps, &(&1["a"] == 66))
Is there some standard function in elixir that filters a list, returning single item if there is only one item … or a list of items if there are numerous items
No, but pattern matching makes it trivial:
def single_or_many([single]), do: single
def single_or_many(many), do: many
And if you wanted to treat the empty list specially (before the 'many' case):
def single_or_many([]), do: :nil
Then, the function call chain would be:
list_of_maps
|> Enum.filter(&(&1["a"] == 1))
|> single_or_many
I am trying to convert an array of strings into an array of maps(string,string) by mapping each element into the array to (element,regex it matches). My code is as follows, however it throws me error when I run it.
var articles:Array[Map[String,String]] = rawArticles map(x => x, x match {
case ArticleRE(id) => id
case _ => " "
}
)).toMap
rawArticles is the original array and ArticleRE is the regex I am matching.
It appears to me that your issue is trying to call toMap on something that isn't a Seq[(A, B)]. Assuming a trivial case like this (it compiles just fine in Scala 2.10 with a few changes):
val rawArticles = Array("articleOne", "articleTwo", "articleThree")
val articleRE = "(.*)".r
/* some place holder value for no match */
val noMatch = ""
val articles = rawArticles map { x => Map(
x -> x match {
case articleRE(id) => (id, articleRE.toString)
case _ => ("", noMatch)
})
}
I think your issue here was trying to convert a Seq that wasn't a Seq of Tuples, you can also directly use case in Map, as Map can take a PartialFunction.
If you are sure about the types that you want then this should work:
var articles = rawArticles.map(x => Map(x -> (x match {
case ArticleRE(id) => id
case _ => " "
})))
I want the last value in list of SML.
for example, 1::2::3 => 3
I tried use "last", but it didn't work.
I tried to implement it, like this:
val rec last =
fn (h::list) => last (list)
| (h::nil) => h
| (nil) => nil;
But it gives me problem: match redundant.
Thank you for your help.
I doubt that the case of (h::nil) is redundant because it is a special case of (h::list). I think you should reorder your match cases:
exception Empty
val rec last =
fn (h::nil) => h
|(h::list) => last (list)
| (nil) => raise Empty;
By the way, List.last should work because it's a part of SML Basis Library.
Could also be done as easy as that:
exception Empty
fun lastinList [] = raise Empty
| lastinList l = hd(rev(l));