When I set immutable hashmap contents on initialization like this:
var result_tags=HashMap[String,Int]()
result_tags=("video"->0,"game"->0,"news"->0,"ee"->0,"sport"->0,
"shop"->0,"ju"->0,"story"->0,"pho"->0,"con"->0,"live"->0,"life"->0,"soft"->0,"hire"->0,"car"->0,
"mm"->0,"mus"->0,"mob"->0,"male"->0,"heal"->0, "sca"->0,"bank"->0,"mail"->0,"cool"->0,"pict"->0, "dl"->0)
It gives me the error:
too many elements for tuple:26,allowed:22
It means the max number of tuples is 22. I know -> is for creating tuples. Is there other ways to initialize hashmap without the limit number of its elements.
What you're actually doing there is initializing a giant Tuple type and trying to assign it to the result_tags variable which is of type HashMap, which wouldn't work even if the tuple size wouldn't exceed
maximum size.
So the error you get regarding tuples is not referring to the -> syntax that you use, but to the number of elements inside the (...) list. You would get the same error even if you wrote it like this:
(("video", 0), ("game", 0), ..., ("dl", 0))
Second, in your case you should do:
var result_tags = HashMap("video" -> 0, "game" -> 0, ..., "dl" -> 0)
(notice that I omitted the type info because Scala infers the type for you.)
The (a1, a2, ..., aN) syntax is an entirely different thing in Scala, since it initializes a tuple type. Every such declaration is converted into a TupleN type, where the maximum size is 22. So the Scala library actually has 22 distinct Tuple classes from Tuple1 to Tuple22.
Third, your style could use some corrections
You should prefer the immutable version of map which is scala.collection.Map
You should prefer immutable variables, which means val instead of var
And less important, but variable names are preferred to be camelCased, so that would be resultTags
Just
var result_tags = Map("video"->0,"game"->0,"news"->0,"ee"->0,"sport"->0,
"shop"->0,"ju"->0,"story"->0,"pho"->0,"con"->0,"live"->0,"life"->0,"soft"->0,"hire"->0,"car"->0,
"mm"->0,"mus"->0,"mob"->0,"male"->0,"heal"->0, "sca"->0,"bank"->0,"mail"->0,"cool"->0,"pict"->0, "dl"->0)
This is how you initialise the Map in Scala
Map initialisation
import scala.collection.immutable.Map
val result_tags = Map("video" -> 0, "game" -> 0, "news" -> 0, "ee" -> 0, "sport" -> 0,
"shop" -> 0, "ju" -> 0, "story" -> 0, "pho" -> 0, "con" -> 0, "live" -> 0, "life" -> 0, "soft" -> 0, "hire" -> 0, "car" -> 0,
"mm" -> 0, "mus" -> 0, "mob" -> 0, "male" -> 0, "heal" -> 0, "sca" -> 0, "bank" -> 0, "mail" -> 0, "cool" -> 0, "pict" -> 0, "dl" -> 0)
HashMap initialisation
import scala.collection.immutable.HashMap
val result_tags = HashMap("video" -> 0, "game" -> 0, "news" -> 0, "ee" -> 0, "sport" -> 0,
"shop" -> 0, "ju" -> 0, "story" -> 0, "pho" -> 0, "con" -> 0, "live" -> 0, "life" -> 0, "soft" -> 0, "hire" -> 0, "car" -> 0,
"mm" -> 0, "mus" -> 0, "mob" -> 0, "male" -> 0, "heal" -> 0, "sca" -> 0, "bank" -> 0, "mail" -> 0, "cool" -> 0, "pict" -> 0, "dl" -> 0)
You could also use the + method in the immutable.HashMap class to achieve what you were trying to do:
From the scala doc:
def +[B1 >: B](elem1: (A, B1), elem2: (A, B1), elems: (A, B1)*): HashMap[A, B1]
Adds two or more elements to this collection and returns a new collection.
val result_tags = HashMap[String,Int]()
val result_tags_filled = result_tags + ("video"->0,"game"->0,"news"->0,"ee"->0,"sport"->0,
"shop"->0,"ju"->0,"story"->0,"pho"->0,"con"->0,"live"->0,"life"->0,"soft"->0,"hire"->0,"car"->0,
"mm"->0,"mus"->0,"mob"->0,"male"->0,"heal"->0, "sca"->0,"bank"->0,"mail"->0,"cool"->0,"pict"->0, "dl"->0)
Related
How to encode java.lang.Object using io.circe
Basically I am trying encode a Map
Map(
"key" -> Option[Json].map(_.asObject).getOrElse(Map.empty) // `object[a -> "x",b -> "y",c -> "z"]`
)
For e.g. I want to convert object[a -> "x",b -> "y",c -> "z"] to {"a":"x", "b":"y","c": "z"}
I have something like the following
struct ht : Hashable {
var x : Int
var y : Int
var z : Int
//Edit added following func following Ahmad's comment
var hashValue: Int { return (x+y+z) }
static func == (lhs: ht, rhs: ht) -> Bool
{
return lhs.x == rhs.x
&& lhs.y == rhs.y
&& lhs.z == rhs.z
}
}
let defaultX = 4
let searchForX = 7
//let searchForX = 1000000
var ss : Set<ht> = [ ht(x:1,y:2,z:3), ht(x:4,y:5,z:6), ht(x:7, y:8, z:100), ht(x:9, y:19, z:12)]
Does Swift have LINQ like functionality where I can search the set ss to retrieve the struct that matches searchForX = 7 as shown above to return (7, 8, 100)? If searchForX = 1000000 I want it to return (4, 5, 6), so failing over to returning the value where defaultX = 4
My current code uses loops. In C# you can use LINQ and specify .FirstOrDefault(...).
Is this possible in Swift?
Thank you
Swift isn't really my thing but a ternary operator similar to the code below may move you along.
var answer = ss.first(where: { $0.x == searchForX }) ?? ss.first(where: { $0.x == defaultX })
Regards
Mike
So the quick answer to your question is
let element = ss.first { $0.x == searchForX } ?? ss.first { $0.x == defaultX }
This can be turned a simple extension
extension Sequence {
func first(_ criteria: (Element) -> Bool, orDefault: (Element) -> Bool) -> Element? {
return first(where: criteria) ?? first(where: orDefault)
}
}
let element = ss.first({ $0.x == searchForX }, orDefault: { $0.x == searchForX })
By making this an extension on Sequence, it will work for Arrays and anything that implements Sequence.
If you are concerned about performance you can roll your own.
extension Sequence {
func first(_ criteria: (Element) -> Bool, orDefault: (Element) -> Bool) -> Element? {
var defaultElement: Element?
for element in self {
if criteria(element) { return element }
if criteria(element) { defaultElement = element }
}
return defaultElement
}
}
You could implement your own custom method to do the desired functionality.
First of all, if you would declare a set of custom structs, note that your struct must be Hashable protocol -which means that it implicitly must be Equatable -, thus it would be implemented as follows:
struct MyStruct: Hashable, Equatable {
var x : Int
var y : Int
var z : Int
// Hashable
var hashValue: Int
// Equatable
static func ==(lhs: MyStruct, rhs: MyStruct) -> Bool {
return lhs.x == rhs.x
}
}
So, you could declare a set of MyStruct.
Probably this is not the right way to assign a value to the hashValue, but for the purpose of answering the question I will assign dummy values to them.
For implementing the method for achieving the desired output, I would recommend to declare it in a set extension (if you would let it also functional for arrays, you could declare it as a Collection extension instead), constrained by the set element type:
extension Set where Element == MyStruct {
func firstOrDefault(for expectedXValue: Int, defaultXValue: Int) -> MyStruct? {
// expeccted
if let expected = first(where: { $0.x == expectedXValue }) {
return expected
}
// default
if let defaultValue = first(where: { $0.x == defaultXValue }) {
return defaultValue
}
// neither
return nil
}
}
Note that if there are more than one expected or default x value in the set, it would returns the first matched one.
You would notice that the main functionality of implementing firstOrDefault method is to use first(where:) method (which is similar to what are you looking for), with a little bit of logic to get a -first- default value instead.
Output:
In case of there is an expected object with x = 7:
let mySet: Set<MyStruct> = [MyStruct(x: 1, y: 2, z: 3, hashValue: 101),
MyStruct(x: 4, y: 5, z: 6, hashValue: 102),
MyStruct(x: 7, y: 8, z: 100, hashValue: 103),
MyStruct(x: 9, y: 19, z: 12, hashValue: 104)]
let myObject = mySet.firstOrDefault(for: 7, defaultXValue: 4)
dump(myObject)
/*
▿ Optional(__lldb_expr_187.MyStruct(x: 7, y: 8, z: 9, hashValue: 102))
▿ some: __lldb_expr_187.MyStruct
- x: 7
- y: 8
- z: 9
- hashValue: 102
*/
In case of there is no expected value and the default x = 4:
let mySet2: Set<MyStruct> = [MyStruct(x: 1, y: 2, z: 3, hashValue: 101),
MyStruct(x: 4, y: 5, z: 6, hashValue: 102),
MyStruct(x: 1000, y: 8, z: 100, hashValue: 103),
MyStruct(x: 9, y: 19, z: 12, hashValue: 104)]
let myObject2 = mySet2.firstOrDefault(for: 7, defaultXValue: 4)
dump(myObject2)
/*
▿ Optional(__lldb_expr_249.MyStruct(x: 4, y: 5, z: 6, hashValue: 102))
▿ some: __lldb_expr_249.MyStruct
- x: 4
- y: 5
- z: 6
- hashValue: 102
*/
In case of there is no expected and no default value:
let mySet3: Set<MyStruct> = [MyStruct(x: 1, y: 2, z: 3, hashValue: 101),
MyStruct(x: 1000, y: 5, z: 6, hashValue: 102),
MyStruct(x: 1000, y: 8, z: 100, hashValue: 103),
MyStruct(x: 9, y: 19, z: 12, hashValue: 104)]
let myObject3 = mySet3.firstOrDefault(for: 7, defaultXValue: 4)
dump(myObject3) // - nil
how would you go about pattern matching to extract values from Maps contained in a List ?
So my data is of type List[Map[String,Any]] and looks like :
List(Map(sequence -> 192, id -> 8697413670252052, type -> List(AimLowEvent, DiscreteEvent), time -> 527638582195))
List(Map(duration -> 143858743, id -> 8702168014834892, sequence -> 195, sessionId -> 8697344444103393, time -> 527780267698, type -> List(SessionCanceled, SessionEnded)), Map(trackingInfo -> Map(trackId -> 14170286, location -> Browse, listId -> cd7c2c7a-00f6-4035-867f-d1dd7d89972d_6625365X3XX1505943605585, videoId -> 80000778, rank -> 0, row -> 0, requestId -> ac12f4e1-5644-46af-87d1-ec3b92ce4896-4071171), id -> 8697344444103393, sequence -> 89, time -> 527636408955, type -> List(Play, Action, Session)), 1)
List(Map(duration -> 142862569, id -> 8702168403395215, sequence -> 201, sessionId -> 8697374208897843, time -> 527780267698, type -> List(SessionCanceled, SessionEnded)), Map(trackingInfo -> Map(trackId -> 14170286, location -> Browse, listId -> cd7c2c7a-00f6-4035-867f-d1dd7d89972d_6625365X3XX1505943605585, videoId -> 80000778, rank -> 0, row -> 0, requestId -> ac12f4e1-5644-46af-87d1-ec3b92ce4896-4071171), id -> 8697374208897843, sequence -> 136, time -> 527637405129, type -> List(Play, Action, Session)), 1)
Firstly I would only like to keep records that contain the Map(trackingInfo -> .. as the fields that are important to me are in those records, e.g trackId. However in those same records I need the outer fields also such as sequence
I've tired to flatten the list into a Map by, so I can match on the maps:
myList.flatten.toMap
However it returns a java.lang.ClassCastException: scala.collection.immutable.Map$Map4 cannot be cast to scala.Tuple2 error.
Any help is appreciated
For what you want to achieve, I believe a filter will do:
// taken from your example and simplified
val list =
List(
Map("duration" -> 142862569L, "id" -> 8702168403395215L, "sequence" -> 201, "sessionId" -> 8697374208897843L, "time" -> 527780267698L, "type" -> List("SessionCanceled", "SessionEnded")),
Map("trackingInfo" -> Map("trackId" -> 14170286L, "location" -> "Browse", "listId" -> "cd7c2c7a-00f6-4035-867f-d1dd7d89972d_6625365X3XX1505943605585", "videoId" -> "80000778", "rank" -> 0, "row" -> 0, "requestId" -> "ac12f4e1-5644-46af-87d1-ec3b92ce4896-4071171"), "id" -> 8697374208897843L, "sequence" -> 136, "time" -> 527637405129L, "type" -> List("Play", "Action", "Session")))
val onlyWithTrackingInfo =
list.filter(_.contains("trackingInfo"))
Printing onlyWithTrackingInfo would output the following (prettified):
List(
Map(
trackingInfo -> Map(trackId -> 14170286, location -> Browse, listId -> cd7c2c7a-00f6-4035-867f-d1dd7d89972d_6625365X3XX1505943605585, videoId -> 80000778, rank -> 0, row -> 0, requestId -> ac12f4e1-5644-46af-87d1-ec3b92ce4896-4071171),
id -> 8697374208897843,
sequence -> 136,
time -> 527637405129,
type -> List(Play, Action, Session)
)
)
One approach would be to collect any Map that consists of the wanted key:
def extract(list: List[Map[String, Any]], key: String) = list.collect{
case m if m.get(key) != None => m
}
val list1: List[Map[String, Any]] = List(
Map("sequence" -> 192, "id" -> 8697413670252052L, "type" -> List("AimLowEvent", "DiscreteEvent"), "time" -> 527638582195L)
)
val list2: List[Map[String, Any]] = List(
Map("duration" -> 143858743, "id" -> 8702168014834892L, "sequence" -> 195, "sessionId" -> 8697344444103393L, "time" -> 527780267698L, "type" -> List("SessionCanceled", "SessionEnded")),
Map("trackingInfo" -> Map("trackId" -> 14170286, "location" -> "Browse", "listId" -> "cd7c2c7a-00f6-4035-867f-d1dd7d89972d_6625365X3XX1505943605585", "videoId" -> 80000778, "rank" -> 0, "row" -> 0, "requestId" -> "ac12f4e1-5644-46af-87d1-ec3b92ce4896-4071171"), "id" -> 8697344444103393L, "sequence" -> 89, "time" -> 527636408955L, "type" -> List("Play", "Action", "Session"))
)
extract(list1, "trackingInfo")
// res1: List[Map[String,Any]] = List()
extract(list2, "trackingInfo")
// res2: List[Map[String,Any]] = List(
// Map(trackingInfo -> Map(trackId -> 14170286, location -> Browse, listId -> cd7c2c7a-00f6-4035-867f-d1dd7d89972d_6625365X3XX1505943605585, videoId -> 80000778, rank -> 0, row -> 0, requestId -> ac12f4e1-5644-46af-87d1-ec3b92ce4896-4071171), id -> 8697344444103393, sequence -> 89, time -> 527636408955, type -> List(Play, Action, Session))
// )
Swift Tuple index using a variable as the index?
Anyone know if it is possible to use a variable as the index for a Swift tuple index. I wish to select and item from a tuple using a random number. I have the random number as a variable but cannot see how to use that as the index for a tuple. I have searched various places already
Make sure you've chosen the correct data structure
If you've reached a point where you need to access tuple members as if "indexed", you should probably look over your data structure to see if a tuple is really the right choice for you in this case. As MartinR mentions in a comment, perhaps an array would be a more appropriate choice, or, as mentioned by simpleBob, a dictionary.
Technically: yes, tuple members can be accessed by index-style
You can make use of runtime introspection to access the children of a mirror representation of the tuple, and choose a child value given a supplied index.
E.g., for 5-tuples where all elements are of same type:
func getMemberOfFiveTuple<T>(tuple: (T, T, T, T, T), atIndex: Int) -> T? {
let children = Mirror(reflecting: tup).children
guard case let idx = IntMax(atIndex)
where idx < children.count else { return nil }
return children[children.startIndex.advancedBy(idx)].value as? T
}
let tup = (10, 11, 12, 13, 14)
let idx = 3
if let member = getMemberOfFiveTuple(tup, atIndex: idx) {
print(member)
} // 13
Note that the type of child.value is Any, hence we need to perform an attempted type conversion back to the element type of the tuple. In case your tuple contains different-type members, the return type cannot be known at compile time, and you'd probably have to keep using type Any for the returned element.
So, what people here are saying - don't use a tuple if you don't actually need one - is correct. However, there is one caveat.
In my experience, using a Swift Array with a static size in performance critical code actually slows down your code by quite a bit. Take a look:
let clz_lookup = [4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
func clz(x : UInt32) -> Int {
guard x != 0 else { return 32 }
var x = x
var n = 0
if (x & 0xFFFF0000) == 0 { n = 16; x <<= 16 } else { n = 0 }
if (x & 0xFF000000) == 0 { n += 8; x <<= 8 }
if (x & 0xF0000000) == 0 { n += 4; x <<= 4 }
return n + clz_lookup[Int(x >> (32 - 4))]
}
This is a fairly straight forward piece of code that uses a cached lookup table at the end. However, if we profile this using instruments we'll see that the lookup is actually much slower than the if statements it replaces! My guess is the reason for that is probably bounds checking or something similar, plus that there is an additional pointer indirection in the implementation.
In any case, that's not good, and since the above is a function that is potentially being called a lot we want to optimise it. A simple technique for that is creating a Tuple instead of an Array, and then simply using withUnsafeMutablePointer to get a pointer directly into the Tuple (which is laid out as a contiguous, static array in memory - exactly what we want in this case!):
var clz_table = (4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0)
let clz_lookup = withUnsafeMutablePointer(to: &clz_table) { $0.withMemoryRebound(to: Int.self, capacity: 15) { $0 } }
This can be used exactly like a regular C-style array in that you can index into it and even change the values at particular indices, but there is no way to grow this array. This method of indexing should be much faster than the other solutions that are mentioning reflection, but is potentially unsafe if used wrong.
Reference: https://gist.github.com/ctreffs/785db636d68a211b25c989644b13f301
In Swift 5:
func tupleToArray<Tuple, Value>(tuple: Tuple) -> [Value] {
let tupleMirror = Mirror(reflecting: tuple)
assert(tupleMirror.displayStyle == .tuple, "Given argument is no tuple")
assert(tupleMirror.superclassMirror == nil, "Given tuple argument must not have a superclass (is: \(tupleMirror.superclassMirror!)")
assert(!tupleMirror.children.isEmpty, "Given tuple argument has no value elements")
return tupleMirror.children.compactMap { (child: Mirror.Child) -> Value? in
let valueMirror = Mirror(reflecting: child.value)
assert(valueMirror.subjectType == Value.self, "Given tuple argument's child type (\(valueMirror.subjectType)) does not reflect expected return value type (\(Value.self))")
return child.value as? Value
}
}
let sss: [String] = tupleToArray(tuple: ("📷", "🍉🍉", "✈️✈️✈️" ,"🍎🍎🍎"))
print(sss)
You can cast a tuple to a buffer only if it has an homogeneous type.
But before doing so, wonder if an array can't do the job.
var tuple: (Int, Int, Int, Int) = (0,1,2,3)
withUnsafeMutablePointer(to: &tuple) { pointer in
pointer.withMemoryRebound(to: Int.self, capacity: 4) { buffer in
buffer[3] = 0
}
}
print(tuple)
result:
(0, 1, 2, 0)
As long as tuple index is just default name, and you can provide your own names
let testTuple = (first: 1, second: 2.0)
let first: Int = testTuple.first
let second: Double = testTuple.second
you can not use variable as index. Pretty close question "can I use KVC for tuple?", and answer - you can't
I have a form in my application that will insert information into the database, but its optional data, it can be nullable. I have this mapping:
case class TransactionFormData(id:Long, transID:Long, operatorID:Long, cardID:Long, stationID:Long, startTime:String, endTime:String, lineNumber:Int, cardNumber:Long, transNumber:Long, ticketNumber:Int, amountEntered:Int, amountMeasured:Int, leaseID_dpID:Int, customerID:Int)
val transForm = Form(
mapping(
"id" -> longNumber,
"transID" -> longNumber,
"operatorID" -> longNumber(min = 0),
"cardID" -> longNumber,
"stationID" -> longNumber,
"startTime" -> nonEmptyText,
"endTime" -> nonEmptyText,
"lineNumber" -> number(min = 0),
"cardNumber" -> longNumber(min = 0),
"transNumber" -> longNumber(min = 0),
"ticketNumber" -> OptionalMapping(number),
"amountEntered" -> number,
"amountMeasured" -> number,
"leaseID_dpID" -> number,
"customerID" -> number
)(TransactionFormData.apply)(TransactionFormData.unapply)
)
from ticketNumber on down, the values can be kept empty, but when I try putting nothing in, the form helper returns an error and requires input, how can I stop this?
Use the optional(number) constraint:
"ticketNumber" -> optional(number),
"amountEntered" -> optional(number),
"amountMeasured" -> optional(number),
"leaseID_dpID" -> optional(number),
"customerID" -> optional(number)
Just as number maps to Int, optional(number) maps to Some[Int]. The constraint allows the field to pass validation if no value is submitted in the form, assigning the field the value None. I appreciate however that this will then require you to change the signature of your case class constructor:
case class TransactionFormData(..., ticketNumber:Option[Int], ...)