How to convert NSSet to [String] array?
I would use map
:
let nss = NSSet(array: ["a", "b", "a", "c"])
let arr = nss.map({ String($0) }) // Swift 2
let arr = map(nss, { "\($0)" }) // Swift 1
Convert NSSet to Swift Array
As of Xcode 7.2 with Swift 2.1.1
Actually, I found out by trial that:
@NSManaged var types : Set<Type>
Works perfectly fine.
The fact is that CoreData generates somethink like:
@NSManaged var types : NSSet
when creating a NSManagedObject
subclass from the editor. So I was wondering if there was a not very verbose method to use the filter
method without casting. But then Swift is supposed to bridge automatically between NSSet, NSArray and their counterparts. So I tried declaring it directly as a Set and it works.
Answering the question, converting it to an Array becomes trivial:
Array(types)
Convert NSSet into an Array
My suggestion is to declare the relationship as non-optional native Swift type
@NSManaged public var children: Set<Node>
To get an array just sort
the set. This is pretty easy with a Swift Set
. As a set is unordered by definition you have to do that anyway to get a fixed order. Assuming you have a dateAdded
attribute you can write
let sortedChildren = children.sorted{$0.dateAdded > $1.dateAdded}
NSSet to string separaing by comma
There is another way to achieve what you are trying to do with fewer lines of code:
You can get an array of NSSet objects using:
NSArray *myArray = [mySet allObjects];
You can convert the array to a string:
NSString *myStr = [myArray componentsJoinedByString:@","];
Converting NSSet to NSMutableArray
func itemMutableArray() -> NSMutableArray {
return NSMutableArray(array: (item.allObjects as! [Item]).sorted{ $0.date.compare($1.date) == NSComparisonResult.OrderedAscending } )
}
Append Values in NSSet To An Array of Type [String]
NSSet
is defined in Objective C, which didn't have generics. It's an untyped collection, so you don't statically know anything about its elements.
As you've noticed, your i
variable isn't a String
, it's an Any
.
You're confusing type coercion ("casting") with type conversion. If i
were a Double
, you could call String(i)
to invoke an initializer which takes a double, and processes into a String.
You tried something similar by calling String(i)
, where you're making the Swift compiler find an initializer on String
with the signitiure init(_: Any)
.
There is no such initializer. And besides, that's not what you want. You don't want to create a new String from a different kind of value. You already have a string, it's just "hidden" behind an Any
reference.
What you're looking for is to do a down-cast, from Any
to String
:
func addStepsToArray() {
if let steps = entity.steps {
for i in steps {
guard let step = i as? String else {
fatalError("Decide what to do if the value isn't a String.")
}
recipeStep.append(i as String)
}
}
}
I'll warn you though, there are several issues/blemishes with this code:
- You're using a
for
loop to do what is ultimately just a mapping operation - Your computation doesn't return its ouput, and instead indirectly achieves its goal through a side-effect on
recipeStep
- Your computation doesn't take a its input as a parameter, and instead indirectly achieves its goal through a side-effect on
entity
i
is conventionally expected to be an integer index of afor
loop iterating over a sequence of numbers. Here it's anAny
(a String at runtime)
Here's what I would suggest instead:
func getRecipeSteps(from entity: MyEntityType) -> [String] {
guard let steps = entity.steps else { return [] }
return steps.map { step in
guard let stringStep = step as? String else {
fatalError("Decide what to do if the value isn't a String.")
}
return step
}
}
Then in the rest of your code (and your tests), you can write self.recipeSteps = getRecipeSteps(from: myEntity)
. Elegant!
If you're certain that these entity.steps
values can only ever be strings, then you can boil this down to a single map
with a force-cast:
func getRecipeSteps(from entity: MyEntityType) -> [String] {
entity.steps?.map { $0 as! String } ?? []
}
Convert NSArray to Swift Array T
I'm currently using
obj.types.allObjects as Type[]
, but that feels like a hack/workaround.
Then revise your feelings. What you're doing is exactly how you do "type-cast / convert the NSArray
to Array<Type>[]
."
An NSArray always arrives into Swift as containing AnyObject
(i.e. an Array<AnyObject>
, aka AnyObject[]
). Casting it down to a specific type, if you know that it contains all one type, is up to you, and how you are doing it is exactly what you are supposed to do.
EDIT In Swift 2.0 and the new iOS 9 APIs, an NSArray will often arrive correctly typed, and no cast will be needed.
EDIT 2 In Swift 3.0, an NSArray that is not correctly typed, though uncommon, will arrive as containing Any
instead of AnyObject
and will still have to be cast down to a specific type.
NSUserDefaults returns swift array [String] instead of Set String
Short answer : No
NSUserDefaults
cannot store sets. It's documented that way and it's because of the limitations of the format used to store the data on disk.
If saving a set works for you and automatically converts it to an array, you're actually lucky as I don't think this is a documented behavior and it should just throw an error.
EDIT : It doesn't work and you should not try it.
The best practice is to convert it to an array before saving and convert it back to a set after retrieving. You could also write a category on NSUserDefaults
that does that automatically. Here is an example with objective-C :
//
// NSUserDefaults+SetAdditions.h
//
#import <Foundation/Foundation.h>
@interface NSUserDefaults (SetAdditions)
- (NSSet *)setForKey:(NSString *)defaultName;
- (void)setSet:(NSSet *)set forKey:(NSString *)defaultName;
@end
//
// NSUserDefaults+SetAdditions.m
//
#import "NSUserDefaults+SetAdditions.h"
@implementation NSUserDefaults (SetAdditions)
- (NSSet *)setForKey:(NSString *)defaultName
{
NSArray *array = [self arrayForKey:defaultName];
if (array) {
return [NSSet setWithArray:array];
} else {
return nil;
}
}
- (void)setSet:(NSSet *)set forKey:(NSString *)defaultName
{
if (set) {
[self setObject:[set allObjects] forKey:defaultName];
} else {
[self setObject:nil forKey:defaultName];
}
}
@end
NSSet with string property from array of objects
You can use valueForKey:
to create an array that contains your id
s and then use that to create an NSSet
NSSet<NSString *> *idSet = [NSSet setWithArray:[arrayOfSomethings valueForKey:@"id"]];
Related Topics
Swiftui Navigationview Trying to Pop to Missing Destination (Monoceros)
Easiest Way to Find Square Root in Swift
Is Code Coverage Already Working for Swift
How to Remove Multiple Items from a Swift Array
Why Is the ! in Swift Called an 'Implicitly' Rather Than 'Explicitly' Unwrapped Optional
How to Specify That a Generic Is a Value Type
Finding the Difference in Time Between Two Nsdates Swift
How to Install Swift Package via Package Manager
Using a Property as a Default Parameter Value for a Method in the Same Class
How to Mock Uiapplication in Swift
Get Path to Swift Script from Within Script
Using Just with Flatmap Produce Failure Mismatch. Combine
Using a Mtltexture as the Environment Map of a Scnscene
Coredata Crash Error Xcode 11 Beta, iOS 13 Beta
Module File's Deployment Target Is iOS9.0 V9.0 with Xcode 7/Swift 2