How to Convert Nsset to [String] Array

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

Swift 2

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:

  1. You're using a for loop to do what is ultimately just a mapping operation
  2. Your computation doesn't return its ouput, and instead indirectly achieves its goal through a side-effect on recipeStep
  3. Your computation doesn't take a its input as a parameter, and instead indirectly achieves its goal through a side-effect on entity
  4. i is conventionally expected to be an integer index of a for loop iterating over a sequence of numbers. Here it's an Any (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 ids and then use that to create an NSSet

NSSet<NSString *> *idSet = [NSSet setWithArray:[arrayOfSomethings valueForKey:@"id"]];


Related Topics



Leave a reply



Submit