I am trying to filter an array containing classes so that only the class that is found in another array will be added to an array. This is what I have so far:
class Match : Equatable {
var name: String
var value: String
init(name: String, value: String) {
self.name = name
self.value = value
}
func ==(lhs: Match, rhs: Match) -> Bool {
return lhs.name == rhs.name && lhs.value == rhs.value
}
// attempt to filter array containing Match structs
let terms = [Match]()
let someOtherObjects = [Match]()
let sampleMatch = Match(name: "someName", value: "someValue")
someOtherObjects.append(sampleMatch)
filteredTerms = terms.filter { term in
if attemptedCombos.contains(sampleMatch) {
return true
}
}
However the compiler does not let me build with the error:
"Cannot convert value of type 'Match' to expected argument type
'#noescape (Match) throws -> Bool'
Any ideas?
Updated using Set (as it seems as if you want the intersection of two [Match] arrays). In addition to Equatable, you must let your Match class conform to Hashable for instances of it to be allowed as elements in a Set.
class Match : Equatable, Hashable {
var name: String
var value: String
init(_ name: String, _ value: String) {
self.name = name
self.value = value
}
var hashValue: Int {
get {
return name.hashValue << 20 + value.hashValue
}
}
}
func ==(lhs: Match, rhs: Match) -> Bool {
return lhs.name == rhs.name && lhs.value == rhs.value
}
Example:
/* Example */
var mySetA : Set<Match> = [Match("foo", "bar"), Match("foo", "foo"), Match("barbar", "foo")]
var mySetB = Set<Match>()
mySetB.insert(Match("barbar", "bar"))
mySetB.insert(Match("bar", "foo"))
mySetB.insert(Match("foo", "bar"))
mySetB.insert(Match("foo", "foo"))
let myIntersect = mySetA.intersect(mySetB)
for match in myIntersect {
print("name: " + match.name + ", value: " + match.value)
}
/* name: foo, value: foo
name: foo, value: bar */
After chatting with the OP we've solved the issue in chat. I'm not sure what the convention are here, but I'll summarize the additional information given by the OP in chat, and the solution to the problem. Consider the block above as a solution to the question above, and the block below as a quite narrow solution to the question above complemented with more specifics from the OP.
The "filter" array of objects are of a different class type (Tern) than the array to be filtered (Match), where these two classes share some class properties.
A natural enquiry to the OP was if it was acceptable to let both these classes have one common superclass; it was.
In addition to the above, one of the properties common for both classes were of a custom enum type, posted in chat by the author.
The final solution used, as above, Set and .intersect():
/* custom enum given by OP in chat */
enum Declension : String {
case firstDeclensionFem = "a:a:am:ae:ae:a:ae:ae:as:arum:is:is"
case secondDeclensionMasc = "us:er:um:i:o:o:i:i:os:orum:is:is"
case secondDeclensionNeu = "um:um:um:i:o:o:a:a:a:orum:is:is"
case thirdDeclensionMasc = " : :em:is:i:e:es:es:es:um:ibus:ibus"
case thirdDeclensionMascSpecial = " : :em:is:i:e:es:es:es:ium:ibus:ibus"
case fourthFem = "us:us:um:us:ui:u:us:us:us:uum:ibus:ibus"
case fourthNeu = "u:u:u:us:u:u:ua:ua:ua:uum:ibus:ibus"
case fifthMasc = "es:es:em:ei:ei:e:es:es:es:erum:ebus:ebus"
case unknown
static let allValues = [firstDeclensionFem, secondDeclensionMasc, secondDeclensionNeu, thirdDeclensionMasc, thirdDeclensionMascSpecial, fourthFem, fourthNeu, fifthMasc]
}
/* use a superclass and let the sets below have members that
are declared to be of this superclass type */
class MyMatchTypes : Equatable, Hashable {
var latin: String
var declension: Declension
init(_ latin: String, _ declension: Declension) {
self.latin = latin
self.declension = declension
}
var hashValue: Int {
get {
return latin.hashValue << 20 + declension.hashValue
}
}
}
func ==(lhs: MyMatchTypes, rhs: MyMatchTypes) -> Bool {
return lhs.latin == rhs.latin && lhs.declension == rhs.declension
}
/* the two classes mentioned in chat: use as subclasses */
class Term : MyMatchTypes {
var meaning: String
var notes: String
var genStem: String
init(_ latin: String, _ declension: Declension, _ meaning: String, _ genStem: String, _ notes: String) {
self.meaning = meaning
self.notes = notes
self.genStem = genStem
super.init(latin, declension)
}
}
class Match : MyMatchTypes {
// ... add stuff
// super init is OK
}
/* Example */
/* ----------------------------------------------- */
/* Set of `Match` objects */
var mySetA = Set<MyMatchTypes>()
mySetA.insert(Match("foo", Declension.firstDeclensionFem))
mySetA.insert(Match("bar", Declension.fourthFem))
mySetA.insert(Match("foofoo", Declension.fourthFem))
mySetA.insert(Match("barbar", Declension.fifthMasc))
/* Set of `Term` objects */
var mySetB = Set<MyMatchTypes>()
mySetB.insert(Term("fooshy", Declension.fourthFem, "a", "b", "c"))
mySetB.insert(Term("barbar", Declension.fifthMasc, "a", "b", "c"))
mySetB.insert(Term("bar", Declension.fourthFem, "a", "b", "c"))
mySetB.insert(Term("foofoo", Declension.firstDeclensionFem, "a", "b", "c"))
mySetB.insert(Term("foobar", Declension.fourthFem, "a", "b", "c"))
/* compute intersection */
let myIntersect = mySetA.intersect(mySetB)
for obj in myIntersect {
print("latin: " + obj.latin + ", declension: \(obj.declension)")
}
/* latin: barbar, declension: fifthMasc
latin: bar, declension: fourthFem */
If Match confirms to Equatable protocol your code should work. Following code compiles successfully:
class Match : Equatable {
var name: String
var value: String
init(name: String, value: String) {
self.name = name
self.value = value
}
}
func ==(lhs: Match, rhs: Match) -> Bool {
return lhs.name == rhs.name && lhs.value == rhs.value
}
let sampleMatch1 = Match(name: "someNameA", value: "someValue")
let sampleMatch2 = Match(name: "someNameA", value: "someValue")
let sampleMatch3 = Match(name: "someNameB", value: "someValue")
let sampleMatch4 = Match(name: "someNameC", value: "someValue")
let terms = [sampleMatch1, sampleMatch3]
var someOtherObjects = [sampleMatch2, sampleMatch4]
let filteredTerms = terms.filter { term in
return someOtherObjects.contains(term)
}
Result is:
Related
My desired end result is to be able to compare to [Server] objects for equality, like so:
let server1 = Server(username: "1")
let server2 = Server(username: "2")
let server1Array = [server1]
let server2Array = [server2]
print(server1Array == server2Array)
I have a class called Server, and I want to overload the == for arrays of Server:
// Server.swift
import Foundation
infix operator ==: AssignmentPrecedence
class Server: ServerRepresentable {
let username: String
init(username: String) {
self.username = username
}
}
// This protocol's only purpose is to allow me to use the `where` clause in the extension below
protocol ServerRepresentable {
var username: String { get }
}
extension Array where Element: ServerRepresentable {
static func == (lhs: Array<ServerRepresentable>, rhs: Array<ServerRepresentable>) {
let server1Usernames = lhs.map { $0.username }
let server2Usernames = rhs.map { $0.username }
// ERROR: Cannot convert value of type '[String]' to expected argument type 'Bool'
return server1Usernames == server2Usernames
}
}
As you can see, I get the error "Cannot convert value of type '[String]' to expected argument type 'Bool'" when I try to perform server1Usernames == server2Usernames, which are both arrays of strings. This is strange because the == operator should already come overloaded in [String] comparisons.
What gives? Is the compiler getting confused because I'm overloading the operator in this class?
There is no need to extend Array. Just make sure your Server conforms to Equatable protocol:
class Server: ServerRepresentable, Equatable {
let username: String
init(username: String) {
self.username = username
}
static func == (lhs: Server, rhs: Server) -> Bool {
lhs.username == rhs.username
}
}
let server1 = Server(username: "1")
let server2 = Server(username: "2")
let server1Array = [server1]
let server2Array = [server2]
print(server1Array == server2Array) // "false\n"
Regarding your question you forgot to add a returning type Bool but the error displayed by the code you have posted should be Unexpected non-void return value in void function
This only works for string, int, double, float arrays.
You could try something like this
To check if there is a difference
for server1 in servers1 {
var name1 = [String]()
var name2 = String()
for server2 in servers2 {name1.append(server2.name)}
name2 = server1.name
if name1.contains(name2) {
print("found duplication")
}
To find the differences
for server1 in servers1 {
var name1 = [String]()
var name2 = String()
for server2 in servers2 {name1.append(server2.name)}
name2 = server1.name
print(name1. difference(name2))
}
//
extension Array where Element: Hashable {
func difference(from other: [Element]) -> [Element] {
let thisSet = Set(self)
let otherSet = Set(other)
return Array(thisSet.symmetricDifference(otherSet))
}
}
I'm trying to write a generic histogram function that operates on an Array, but I'm running into difficulties as Type 'Element' does not conform to protocol 'Hashable'.
extension Array {
func histogram() -> [Array.Element: Int] {
return self.reduce([Array.Element: Int]()) { (acc, key) in
let value = (acc[key] == nil) ? 1 : (acc[key]! + 1)
return acc.dictionaryByUpdatingKey(key: key, value: value)
}
}
}
where dictionaryByUpdatingKey(...) mutates an existing dictionary as follows:
extension Dictionary {
func dictionaryByUpdatingKey(key: Dictionary.Key, value: Dictionary.Value) -> Dictionary {
var mutableSelf = self
let _ = mutableSelf.updateValue(value, forKey: key)
return mutableSelf
}
}
I have tried replacing Array.Element with AnyHashable and then forcing the key as! AnyHashable, but this seems messy and the return type should preferably be of the same type as the Array.Element and not of AnyHashable.
I wish to use the Array extension as follows:
let names = ["Alex", "Alex", "James"]
print(names.histogram()) // ["James": 1, "Alex": 2]
or
let numbers = [2.0, 2.0, 3.0]
print(numbers.histogram()) // [3.0: 1, 2.0: 2]
Add the generic where clause: where Element: Hashable to your extension:
extension Sequence where Element: Hashable {
func histogram() -> [Element: Int] {
return self.reduce([Element: Int]()) { (acc, key) in
let value = acc[key, default: 0] + 1
return acc.dictionaryByUpdatingKey(key: key, value: value)
}
}
}
I also incorporated #MartinR's suggestion of using the new default value for dictionary look ups.
Using reduce(into:_:) you can do this much more simply and efficiently:
extension Sequence where Element: Hashable {
func histogram() -> [Element: Int] {
return self.reduce(into: [:]) { counts, elem in counts[elem, default: 0] += 1 }
}
}
First, you could limit the Element type to only be Hashable.
extension Array where Array.Element:Hashable {
After this, you might get another error because the swift compiler is a little "overstrained". Try to give him a hint:
typealias RT = [Array.Element: Int]
and use it everywhere. So:
extension Array where Array.Element:Hashable {
typealias RT = [Array.Element: Int]
func histogram() -> RT {
return self.reduce(RT()) { (acc, key) in
let value = (acc[key] == nil) ? 1 : (acc[key]! + 1)
return acc.dictionaryByUpdatingKey(key: key, value: value)
}
}
}
finally should work.
Swift has the OptionSet type, which basically adds set operations to C-Style bit flags. Apple is using them pretty extensively in their frameworks. Examples include the options parameter in animate(withDuration:delay:options:animations:completion:).
On the plus side, it lets you use clean code like:
options: [.allowAnimatedContent, .curveEaseIn]
However, there is a downside as well.
If I want to display the specified values of an OptionSet, there doesn't seem to be a clean way to do it:
let options: UIViewAnimationOptions = [.allowAnimatedContent, .curveEaseIn]
print("options = " + String(describing: options))
Displays the very unhelpful message:
options = UIViewAnimationOptions(rawValue: 65664)
The docs for some of these bit fields expresses the constant as a power-of-two value:
flag0 = Flags(rawValue: 1 << 0)
But the docs for my example OptionSet, UIViewAnimationOptions, doesn't tell you anything about the numeric value of these flags and figuring out bits from decimal numbers is not straightforward.
Question:
Is there some clean way to map an OptionSet to the selected values?
My desired output would be something like:
options = UIViewAnimationOptions([.allowAnimatedContent, .curveEaseIn])
But I can't think of a way to do this without adding messy code that would require me to maintain a table of display names for each flag.
(I'm interested in doing this for both system frameworks and custom OptionSets I create in my own code.)
Enums let you have both a name and a raw value for the enum, but those don't support the set functions you get with OptionSets.
Here is one approach I've taken, using a dictionary and iterating over the keys. Not great, but it works.
struct MyOptionSet: OptionSet, Hashable, CustomStringConvertible {
let rawValue: Int
static let zero = MyOptionSet(rawValue: 1 << 0)
static let one = MyOptionSet(rawValue: 1 << 1)
static let two = MyOptionSet(rawValue: 1 << 2)
static let three = MyOptionSet(rawValue: 1 << 3)
var hashValue: Int {
return self.rawValue
}
static var debugDescriptions: [MyOptionSet:String] = {
var descriptions = [MyOptionSet:String]()
descriptions[.zero] = "zero"
descriptions[.one] = "one"
descriptions[.two] = "two"
descriptions[.three] = "three"
return descriptions
}()
public var description: String {
var result = [String]()
for key in MyOptionSet.debugDescriptions.keys {
guard self.contains(key),
let description = MyOptionSet.debugDescriptions[key]
else { continue }
result.append(description)
}
return "MyOptionSet(rawValue: \(self.rawValue)) \(result)"
}
}
let myOptionSet = MyOptionSet([.zero, .one, .two])
// prints MyOptionSet(rawValue: 7) ["two", "one", "zero"]
This article in NSHipster gives an alternative to OptionSet that offers all the features of an OptionSet, plus easy logging:
https://nshipster.com/optionset/
If you simply add a requirement that the Option type be CustomStringConvertible, you can log Sets of this type very cleanly. Below is the code from the NSHipster site - the only change being the addition of CustomStringConvertible conformance to the Option class
protocol Option: RawRepresentable, Hashable, CaseIterable, CustomStringConvertible {}
enum Topping: String, Option {
case pepperoni, onions, bacon,
extraCheese, greenPeppers, pineapple
//I added this computed property to make the class conform to CustomStringConvertible
var description: String {
return ".\(self.rawValue)"
}
}
extension Set where Element == Topping {
static var meatLovers: Set<Topping> {
return [.pepperoni, .bacon]
}
static var hawaiian: Set<Topping> {
return [.pineapple, .bacon]
}
static var all: Set<Topping> {
return Set(Element.allCases)
}
}
typealias Toppings = Set<Topping>
extension Set where Element: Option {
var rawValue: Int {
var rawValue = 0
for (index, element) in Element.allCases.enumerated() {
if self.contains(element) {
rawValue |= (1 << index)
}
}
return rawValue
}
}
Then using it:
let toppings: Set<Topping> = [.onions, .bacon]
print("toppings = \(toppings), rawValue = \(toppings.rawValue)")
That outputs
toppings = [.onions, .bacon], rawValue = 6
Just like you want it to.
That works because a Set displays its members as a comma-delimited list inside square brackets, and uses the description property of each set member to display that member. The description property simply displays each item (the enum's name as a String) with a . prefix
And since the rawValue of a Set<Option> is the same as an OptionSet with the same list of values, you can convert between them readily.
I wish Swift would just make this a native language feature for OptionSets.
StrOptionSet Protocol:
Add a labels set property to test each label value on Self.
StrOptionSet Extension:
Filter out which is not intersected.
Return the label text as array.
Joined with "," as CustomStringConvertible::description
Here is the snippet:
protocol StrOptionSet : OptionSet, CustomStringConvertible {
typealias Label = (Self, String)
static var labels: [Label] { get }
}
extension StrOptionSet {
var strs: [String] { return Self.labels
.filter{ (label: Label) in self.intersection(label.0).isEmpty == false }
.map{ (label: Label) in label.1 }
}
public var description: String { return strs.joined(separator: ",") }
}
Add the label set for target option set VTDecodeInfoFlags.
extension VTDecodeInfoFlags : StrOptionSet {
static var labels: [Label] { return [
(.asynchronous, "asynchronous"),
(.frameDropped, "frameDropped"),
(.imageBufferModifiable, "imageBufferModifiable")
]}
}
Use it
let flags: VTDecodeInfoFlags = [.asynchronous, .frameDropped]
print("flags:", flags) // output: flags: .asynchronous,frameDropped
This is how I did it.
public struct Toppings: OptionSet {
public let rawValue: Int
public static let cheese = Toppings(rawValue: 1 << 0)
public static let onion = Toppings(rawValue: 1 << 1)
public static let lettuce = Toppings(rawValue: 1 << 2)
public static let pickles = Toppings(rawValue: 1 << 3)
public static let tomatoes = Toppings(rawValue: 1 << 4)
public init(rawValue: Int) {
self.rawValue = rawValue
}
}
extension Toppings: CustomStringConvertible {
static public var debugDescriptions: [(Self, String)] = [
(.cheese, "cheese"),
(.onion, "onion"),
(.lettuce, "lettuce"),
(.pickles, "pickles"),
(.tomatoes, "tomatoes")
]
public var description: String {
let result: [String] = Self.debugDescriptions.filter { contains($0.0) }.map { $0.1 }
let printable = result.joined(separator: ", ")
return "\(printable)"
}
}
struct MyOptionSet: OptionSet {
let rawValue: UInt
static let healthcare = MyOptionSet(rawValue: 1 << 0)
static let worldPeace = MyOptionSet(rawValue: 1 << 1)
static let fixClimate = MyOptionSet(rawValue: 1 << 2)
static let exploreSpace = MyOptionSet(rawValue: 1 << 3)
}
extension MyOptionSet: CustomStringConvertible {
static var debugDescriptions: [(Self, String)] = [
(.healthcare, "healthcare"),
(.worldPeace, "world peace"),
(.fixClimate, "fix the climate"),
(.exploreSpace, "explore space")
]
var description: String {
let result: [String] = Self.debugDescriptions.filter { contains($0.0) }.map { $0.1 }
return "MyOptionSet(rawValue: \(self.rawValue)) \(result)"
}
}
Usage
var myOptionSet: MyOptionSet = []
myOptionSet.insert(.healthcare)
print("here is my options: \(myOptionSet)")
I tried to create a custom iterator which returns wrapper abcContainer over raw data class abc
// raw data class
class abc {
var name : String = "";
init( _ value : String) {
name = value;
}
}
// with container, only "name" is to be visible
class abcContainer {
private var _abc : abc;
init( _ obj : abc) {
_abc = obj;
}
// + extra methods here
func getName() -> String {
return _abc.name
}
}
The point would be that the dictionary would return instances of abcContainer instead of just the plain raw abc class.
I wanted to use the sequence protocol to make the conversion automatic, but I was not able to transform the [String:abc] into [String:abcContainer] automatically like this:
// the iterator is implemented just iterating the inner basic dict
// but wrapping the result value as abcContainer
class abcIterator : Sequence, IteratorProtocol {
private var __source : [String:abc]?;
var index = 0
var myIterator : DictionaryIterator<String, abc>;
init(_ ctxArray: [String:abc]) {
self.__source = ctxArray
index = 0;
myIterator = (__source?.makeIterator())!
}
func next() -> abcContainer? {
let nextItem = myIterator.next();
if(nextItem != nil) {
return abcContainer((nextItem?.value)!);
}
return nil;
}
}
// this was supposed to be the wrapper over the collection
class abcCollection : Sequence {
private var __source : [String:abc]?;
init(_ list: [String:abc]) {
self.__source = list
}
func makeIterator() -> abcIterator {
return abcIterator(self.__source!);
}
}
I'm probably missing something very basic here. When I try to use the collection like this:
var dict : [String:abc] = [String:abc]();
dict["abba"] = abc("John Smith");
for (key,value) in abcCollection(dict) {
print(key, value.getName());
}
I get error: Expression type "abcCollection" is ambiguous without more context
Does anyone have idea how to make it work? What is missing? I have a feeling that this answer has the information I need...
Swift 2 to 3 Migration for Swift Sequence Protocol
The problem in your original code is that abcCollection(dict)
returned a sequence of abcContainer objects, and those cannot
be assigned to a (key, value) tuple.
You can achieve your goal with
class abcCollection : Sequence {
private var __source : [String:abc]
init(_ list: [String:abc]) {
self.__source = list
}
public func makeIterator() -> AnyIterator<(AnyObject,abcContainer)> {
let mapped = self.__source.lazy.map {
($0.key as AnyObject, abcContainer($0.value))
}
return AnyIterator(mapped.makeIterator())
}
}
Making __source non-optional makes all the (optional) unwrappings
redundant, and lazy.map { ... } returns a lazily evaluated
sequence of key/value pairs which is then type-erased.
Ok, perhaps the answer was abcIterator was not necessary, you could have defined the iterator directly just like done in the linked answer like this:
class abcCollection : Sequence {
private var __source : [String:abc]?;
init(_ list: [String:abc]) {
self.__source = list
}
public func makeIterator() -> AnyIterator<(AnyObject,abcContainer)> {
var it = self.__source?.makeIterator();
return AnyIterator {
let n = it?.next();
if n == nil { return nil }
return (n?.key as AnyObject, abcContainer((n?.value)!))
}
}
}
After that, the custom collection returned wrapped objects correctly.
I'd like to use an array of custom enums as a dictionary key, but I'm having difficulty figuring out how to make the array conform to Hashable. The compiler tells me that [Symbol] doesn't conform to Hashable. What do I have to do to get this to compile?
I messed around with an extension to Array where Element:Symbol, but I couldn't figure out how to add a protocol that way.
enum Symbol:Hashable, Equatable {
case Dot
case Dash
var value:Int {
get {
switch self {
case .Dot: return 0
case .Dash: return 1
}
}
var hashValue:Int {
return self.value
}
}
func ==(left: Symbol, right: Symbol) -> Bool {
return left.value == right.value
}
struct MorseDictionary {
static let symbolToStringDictionary:[[Symbol]:String] = [
[.Dot, .Dash]:"A"
]
}
In Swift 2.2 you cannot create an extension restricted to an Array of a specific type
So you cannot conform an Array of Symbol to Hashable or Equatable.
This means you cannot use an Array of Symbol as key in a Dictionary.
Of course you can create an extension making Array (every array!!) Equatable and Hashable but it's a crazy approach since obviously you will never be able to provide a valid implementation.
However you can follow another approach
First of all your enum can be rewritten this way
enum Symbol: Int {
case Dot = 0, Dash
}
Next you'll need a wrapper for an array of Symbol
struct Symbols: Hashable, Equatable {
let value: [Symbol]
// you can use a better logic here
var hashValue: Int { return value.first?.hashValue ?? 0 }
}
func ==(left: Symbols, right:Symbols) -> Bool {
return !zip(left.value, right.value).contains { $0.0 != $0.1 }
}
Now you can create your dictionary
let dict: [Symbols:String] = [Symbols(value: [.Dot, .Dash]) : "A"]
I have same idea with #appzYourLife. So do not accept my answer. The worth thing here, I have implemented the hash function for Symbols.
func ==(left: Symbols, right: Symbols) -> Bool {
return left.value == right.value
}
enum Symbol: Int {
case Dot = 0
case Dash = 1
case Count = 2
}
struct Symbols: Hashable {
let symbols: [Symbol]
init(symbols: [Symbol]) {
self.symbols = symbols
}
var value: Int {
var sum = 0
var i = 1
symbols.forEach({ (s) in
sum += s.rawValue * i
i = i * Symbol.Count.rawValue
})
return sum
}
var hashValue: Int {
return value % Int(pow(Double(2), Double(Symbol.Count.rawValue)))
}
}
struct MorseDictionary {
static let symbolToStringDictionary: [Symbols: String] = [
Symbols(symbols: [.Dot, .Dash]): "A",
Symbols(symbols: [.Dash, .Dot]): "B",
Symbols(symbols: [.Dash, .Dash]): "C",
Symbols(symbols: [.Dot, .Dot]): "D",
]
}
Clients's code:
MorseDictionary.symbolToStringDictionary[Symbols(symbols: [.Dot, .Dash])]
MorseDictionary.symbolToStringDictionary[Symbols(symbols: [.Dash, .Dot])]
MorseDictionary.symbolToStringDictionary[Symbols(symbols: [.Dash, .Dash])]
MorseDictionary.symbolToStringDictionary[Symbols(symbols: [.Dot, .Dot])]
Results in:
"A"
"B"
"C"
"D"