I want to check if a string exists in any of the values in my Dictionary
Dictionary<String, AnyObject>
I know arrays has .contains so I would think a dictionary does too. Xcode tells me to use the following when I start typing contains
countDic.contains(where: { ((key: String, value: AnyObject)) -> Bool in
<#code#>
})
I just don't understand how to use this I know inside I need to return a Bool, but I don't understand where I put what String I'm looking for. Any help would be great.
contains(where:) checks if any element of the collection satisfies
the given predicate, so in your case it would be
let b = countDic.contains { (key, value) -> Bool in
value as? String == givenString
}
or, directly applied to the values view of the dictionary:
let b = countDic.values.contains { (value) -> Bool in
value as? String == givenString
}
In both cases it is necessary to (optionally) cast the AnyObject
to a String in order to compare it with the given string.
It would be slightly easier with a dictionary of type
Dictionary<String, String> because strings are Equatable,
and the contains(element:) method can be used:
let b = countDic.values.contains(givenString)
Since your values are AnyObject – Any in Swift 3 - you have to check if the value is a string. If yes check if the value contains the substring.
let countDic : [String:Any] = ["alpha" : 1, "beta" : "foo", "gamma" : "bar"]
countDic.contains { (key, value) -> Bool in
if let string = value as? String { return string.contains("oo") }
return false
}
However if you want to check if any of the values is equal to (rather than contains) a string you could use also the filter function and isEmpty
!countDic.filter { (key, value) -> Bool in
value as? String == "foo"
}.isEmpty
You may need to learn basic usage of contains(where:) for Dictionarys first:
For [String: Int]:
let myIntDict1: [String: Int] = [
"a" : 1,
"b" : 101,
"c" : 2
]
let myIntDict1ContainsIntGreaterThan100 = myIntDict1.contains {
key, value in //<- `value` is inferred as `Int`
value > 100 //<- true when value > 100, false otherwise
}
print(myIntDict1ContainsIntGreaterThan100) //->true
For [String: String]:
let myStringDict1: [String: String] = [
"a" : "abc",
"b" : "def",
"c" : "ghi"
]
let myStringDict1ContainsWordIncludingLowercaseE = myStringDict1.contains {
key, value in //<- `value` is inferred as `String`
value.contains("e") //<- true when value contains "e", false otherwise
}
print(myStringDict1ContainsWordIncludingLowercaseE) //->true
So, with [String: AnyObject]:
let myAnyObjectDict1: [String: AnyObject] = [
"a" : "abc" as NSString,
"b" : 101 as NSNumber,
"c" : "ghi" as NSString
]
let myAnyObjectDict1ContainsWordIncludingLowercaseE = myAnyObjectDict1.contains {
key, value in //<- `value` is inferred as `AnyObject`
//`AnyObject` may not have the `contains(_:)` method, so you need to check with `if-let-as?`
if let stringValue = value as? String {
return value.contains("e") //<- true when value is a String and contains "e"
} else {
return false //<- false otherwise
}
}
print(myAnyObjectDict1ContainsWordIncludingLowercaseE) //->false
So, in your case:
let countDic: [String: AnyObject] = [
"a" : 1 as NSNumber,
"b" : "no" as NSString,
"c" : 2 as NSNumber
]
let countDicContainsString = countDic.contains {
key, value in //<- `value` is inferred as `AnyObject`
value is String //<- true when value is a String, false otherwise
}
print(countDicContainsString) //->true
Related
I have a type defined as follows:
type Employee = {
Id: Guid
Name: string
Phone: string
Email: Option<string>
}
and an instance of this type:
let emp = {
Id = Guid "bc07e94c-b376-45a2-928b-508b888802c9"
Name = "A"
Phone = "B"
Email = Some "E"
}
I want to extract the field names and values from this record type using reflection like the following:
let getFieldValueMappingOfARecordType (data: 'T) : seq<string * obj> =
let fieldValueMapping =
data.GetType()
|> FSharpType.GetRecordFields
|> Seq.map (
fun propertyInfo ->
(propertyInfo.Name, data |> propertyInfo.GetValue)
)
fieldValueMapping
Then invoking the above function with the instance of employee type
let mapping = getFieldValueMappingOfARecordType emp
|> Seq.toList
gives us:
val mapping : (string * obj) list =
[("Id", bc07e94c-b376-45a2-928b-508b888802c9); ("Name", "A"); ("Phone", "B");
("Email", Some "E")]
So far it's working well with non-optional type. But in case of optional types, it's returning the value of the field as either Some value or None. What I would like to do is to get the value when the field has Some value or make it null when it's None.
Essentially like the follwing:
val mapping : (string * obj) list =
[("Id", bc07e94c-b376-45a2-928b-508b888802c9); ("Name", "A"); ("Phone", "B");
("Email", "E")]
Or if the employee instance is like the following:
let emp = {
Id = Guid "bc07e94c-b376-45a2-928b-508b888802c9"
Name = "A"
Phone = "B"
Email = None
}
Then,
val mapping : (string * obj) list =
[("Id", bc07e94c-b376-45a2-928b-508b888802c9); ("Name", "A"); ("Phone", "B");
("Email", null)]
This is what I have so far (non-working code):
open System
open Microsoft.FSharp.Reflection
open System.Reflection
type Employee = {
Id: Guid
Name: string
Phone: string
Email: Option<string>
}
let emp = {
Id = Guid "bc07e94c-b376-45a2-928b-508b888802c9"
Name = "A"
Phone = "B"
Email = Some "E"
}
let getSomeOrNull (t: Type) (o: obj) =
let opt = typedefof<option<_>>.MakeGenericType [| t |]
match (o :?> opt) with
| Some s ->
s
| None ->
null
let getValues (data: 'T) =
let values =
data.GetType()
|> FSharpType.GetRecordFields
|> Array.map (
fun propertyInfo ->
let value =
data |> propertyInfo.GetValue
let isOption =
propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() = typedefof<Option<_>>
match isOption with
| true ->
(propertyInfo.Name, (getSomeOrNull propertyInfo.PropertyType value))
| false ->
(propertyInfo.Name, value)
)
values
getValues emp
|> printfn "%A"
I think the only way to do this is with reflection:
let getSomeOrNull (t: Type) (o: obj) =
if isNull o then null
else t.GetProperty("Value").GetValue(o)
I think this should do the trick:
let getSomeOrNull (o: obj) =
match o with
| :? Option<string> as o -> a |> Option.toObj > box
| _ -> null
Is there an equivalent to Java's Wildcards in Flow?
Here's my example code I've been working on as a test:
type InterfaceType = {
var1 : number,
};
type ActualType = InterfaceType & {
var2 : string,
};
type InterfaceGenericType<T : InterfaceType> = {
var3 : T,
}
type ActualGenericType = InterfaceGenericType<ActualType> & {
}
class State<T : InterfaceGenericType<InterfaceType>> {
prop : T;
constructor(arg : T) : State<T> {
this.prop = arg;
return this;
}
}
let actual : ActualType = {
var1: 1,
var2: "two",
};
let actualGeneric : ActualGenericType = {
var3 : actual,
}
let s2 = new State(actualGeneric);
This is the flow error I'm getting:
40: let s2 = new State(actualGeneric);
^ Cannot call `State` with `actualGeneric` bound to `arg` because property `var2` is missing in `InterfaceType` [1] but exists in object type [2] in property `var3`.
References:
20: class State<T : InterfaceGenericType<InterfaceType>> {
^ [1]
7: type ActualType = InterfaceType & {
^ [2]
I know I can get around the issue by doing:
class State<I : InterfaceType, T : InterfaceGenericType<I>> {
but I'm trying to not have to declare both types.
We can trim down your code a bit to remove the class:
type InterfaceType = { var1: number };
type ActualType = InterfaceType & { var2: string, };
type InterfaceGenericType<T : InterfaceType> = {
var3: T,
};
let actual: ActualType = {
var1: 1,
var2: "two",
};
let actualGeneric: InterfaceGenericType<ActualType> = {
var3: actual,
};
let v: InterfaceGenericType<InterfaceType> = actualGeneric;
I can't speak to Java since I don't know it, but I can tell you how to fix this. If we look at the error for this code:
17: let v: InterfaceGenericType<InterfaceType> = actualGeneric;
^ Cannot assign `actualGeneric` to `v` because property `var2` is missing in `InterfaceType` [1] but exists in object type [2] in type argument `T` [3].
References:
17: let v: InterfaceGenericType<InterfaceType> = actualGeneric;
^ [1]
2: type ActualType = InterfaceType & { var2: string, };
^ [2]
4: type InterfaceGenericType<T : InterfaceType> = {
^ [3]
The core issue is that v's type InterfaceGenericType<InterfaceType> would for example allow you to do
v.var3 = { var1: 42 };
because that is a valid InterfaceType object. That isn't a valid ActualType object, but by assigning actualGeneric to v, you've essentially erased that type information, which means that if your code were allowed as-is, the assignment would corrupt your actualGeneric object's type.
The fix for this is to tell Flow that the var3 property is read-only, by changing
var3: T,
to be
+var3: T,
I have a mutableAttributedString in which few strings are linkAttributed,I want to find the Range of all link attributed string. How to do that in swift3 ?
When user start type # in textview i show list of few name. If user select any row then following method gets called.
func didSelectMemberId(_ model: BaseModel) {
var fullName = ""
if model.entityType == ReceiverType.Active.rawValue{
fullName = model.name
}else{
fullName = AtMention + model.name + " " + model.name2
}
let attributedString = NSMutableAttributedString(string:fullName, attributes:[NSFontAttributeName:(appNeedsAutoResize ? (UIUtils.getFontForApproprieteField(.headlineWithoutBold).font) : UIFont.systemFont(ofSize: 14))])
attributedString.addAttribute(NSLinkAttributeName, value: "connectmention://\(model.entityId.stringValue())", range: NSRange(location: 0, length: fullName.length))
attributedString.append(NSAttributedString(string: emptySpaceStringByUC, attributes:[NSFontAttributeName:(appNeedsAutoResize ? (UIUtils.getFontForApproprieteField(.headlineWithoutBold).font) : UIFont.systemFont(ofSize: 14))]))
self.composeBar.textView.textStorage.insert(attributedString, at:self.composeBar.textView.selectedRange.location)
}
self.composeBar.textView.selectedRange = NSMakeRange(self.composeBar.textView.selectedRange.location+fullName.length, 0 )
To get the link proprty I am using the following method
func getlinkActionRange(attributeString: NSAttributedString) -> [MentionStruct] {
var arrMentions = [MentionStruct]()
_ = attributeString.enumerateAttribute(NSLinkAttributeName, in: NSRange.init(location: 0, length: attributeString.length), options: [], using: { (value, range, stop) in
if let url = value {
let occurrence = (attributeString.string as NSString).substring(with:range)
arrMentions.append(MentionStruct(link: url as! String, text: occurrence, range: range))
}
})
return arrMentions
}
If user type anything after inserting that name , that type string also coming.
I have a dictionary.
var params: [String: [String]] = [:]
I assign an array to the first key and the first key only. Now print params prints :
["names" : ["jack", "joe", "jill"]]
How do I iterate through this array at this given key so that I could loop through and print jack, joe, and jill?
for (key, names) in params {
for name in names {
print("\(name))
}
}
var params: [String: [String]] = ["names" : ["jack", "joe", "jill"]]
for key in params.keys {
NSLog("%#", key)
let list = params[key]
for obj in list! {
NSLog("%#", obj)
}
}
Using tuples:
let params: [String: [String]] = ["names" : ["jack", "joe", "jill"]]
for (key, names) in params {
print("\(key)")
for name in names {
print("\(name)")
}
}
So it looks like the filter function on a Swift (2.x) dictionary returns a tuple array. My question is there an elegant solution to turning it back into a dictionary? Thanks in advance.
let dictionary: [String: String] = [
"key1": "value1",
"key2": "value2",
"key3": "value3"
]
let newTupleArray: [(String, String)] = dictionary.filter { (tuple: (key: String, value: String)) -> Bool in
return tuple.key != "key2"
}
let newDictionary: [String: String] = Dictionary(dictionaryLiteral: newTupleArray) // Error: cannot convert value of type '[(String, String)]' to expected argument type '[(_, _)]'
If you are looking for a more functional approach:
let result = dictionary.filter {
$0.0 != "key2"
}
.reduce([String: String]()) { (var aggregate, elem) in
aggregate[elem.0] = elem.1
return aggregate
}
reduce here is used to construct a new dictionary from the filtered tuples.
Edit: since var parameters has been deprecated in Swift 2.2, you need to create a local mutable copy of aggregate:
let result = dictionary.filter {
$0.0 != "key2"
}
.reduce([String: String]()) { aggregate, elem in
var newAggregate = aggregate
newAggregate[elem.0] = elem.1
return newAggregate
}
You can extend Dictionary so that it takes a sequence of tuples as initial values:
extension Dictionary {
public init<S: SequenceType where S.Generator.Element == (Key, Value)>(_ seq: S) {
self.init()
for (k, v) in seq { self[k] = v }
}
}
and then do
let newDictionary = Dictionary(newTupleArray)