Unfortunately in Actionscript, it seems like support for the Vector class isn't fully there yet. There are some scenarios where I need to convert a Vector into an array (creating an ArrayCollection for example). I thought this would do the trick:
var myVector:Vector.<MyType> = new Vector.<MyType>();
var newArray:Array = new Array(myVector);
Apparently this just creates an array where the first index of the array contains the full Vector object. Is this my only option:
var newArray:Array = new Array(myVector);
for each(var item:MyType in myVector)
{
newArray.push(item);
}
I feel like that clutters up the code a lot and I need to do this in a lot of places. The Vector class doesn't implement any kind of interface, so as far as I can tell I can't create a generic function to convert to an array. Is there any way to do this without adding this mess every time I want to convert a Vector to an array?
There's no easy/fast way to do it, the best solution is to use an utility class like this one:
package {
public class VectorUtil {
public static function toArray(obj:Object):Array {
if (!obj) {
return [];
} else if (obj is Array) {
return obj as Array;
} else if (obj is Vector.<*>) {
var array:Array = new Array(obj.length);
for (var i:int = 0; i < obj.length; i++) {
array[i] = obj[i];
}
return array;
} else {
return [obj];
}
}
}
}
Then you just have to update your code to something like this:
var myArray:Array = VectorUtil.toArray(myVector);
Paul at Work found a better way to do it.
var newArray:Array = [].concat(myVector);
Related
I am still not sure about the rules of struct copy or reference.
I want to mutate a struct object while iterating on it from an array:
For instance in this case I would like to change the background color
but the compiler is yelling at me
struct Options {
var backgroundColor = UIColor.blackColor()
}
var arrayOfMyStruct = [MyStruct]
...
for obj in arrayOfMyStruct {
obj.backgroundColor = UIColor.redColor() // ! get an error
}
struct are value types, thus in the for loop you are dealing with a copy.
Just as a test you might try this:
Swift 3:
struct Options {
var backgroundColor = UIColor.black
}
var arrayOfMyStruct = [Options]()
for (index, _) in arrayOfMyStruct.enumerated() {
arrayOfMyStruct[index].backgroundColor = UIColor.red
}
Swift 2:
struct Options {
var backgroundColor = UIColor.blackColor()
}
var arrayOfMyStruct = [Options]()
for (index, _) in enumerate(arrayOfMyStruct) {
arrayOfMyStruct[index].backgroundColor = UIColor.redColor()
}
Here you just enumerate the index, and access directly the value stored in the array.
Hope this helps.
You can use use Array.indices:
for index in arrayOfMyStruct.indices {
arrayOfMyStruct[index].backgroundColor = UIColor.red
}
You are working with struct objects which are copied to local variable when using for in loop. Also array is a struct object, so if you want to mutate all members of the array, you have to create modified copy of original array filled by modified copies of original objects.
arrayOfMyStruct = arrayOfMyStruct.map { obj in
var obj = obj
obj.backgroundColor = .red
return obj
}
It can be simplified by adding this Array extension.
Swift 4
extension Array {
mutating func mutateEach(by transform: (inout Element) throws -> Void) rethrows {
self = try map { el in
var el = el
try transform(&el)
return el
}
}
}
Usage
arrayOfMyStruct.mutateEach { obj in
obj.backgroundColor = .red
}
For Swift 3, use the enumerated() method.
For example:
for (index, _) in arrayOfMyStruct.enumerated() {
arrayOfMyStruct[index].backgroundColor = UIColor.redColor()
}
The tuple also includes a copy of the object, so you could use for (index, object) instead to get to the object directly, but since it's a copy you would not be able to mutate the array in this way, and should use the index to do so. To directly quote the documentation:
If you need the integer index of each item as well as its value, use
the enumerated() method to iterate over the array instead. For each
item in the array, the enumerated() method returns a tuple composed of
an integer and the item.
Another way not to write subscript expression every time.
struct Options {
var backgroundColor = UIColor.black
}
var arrayOfMyStruct = [Options(), Options(), Options()]
for index in arrayOfMyStruct.indices {
var option: Options {
get { arrayOfMyStruct[index] }
set { arrayOfMyStruct[index] = newValue }
}
option.backgroundColor = .red
}
I saw this method in some code and it seems to be working
for (var mutableStruct) in arrayOfMyStruct {
mutableStruct.backgroundColor = UIColor.redColor()
}
I am trying to port an existing Swift code to Kotlin and I'd like to use best practice for the following Swift code:
struct Deck {
private(set) var cards: [Card]
var cardsCount: Int {
return self.cards.count
}
init(cards: [Card] = []) {
self.cards = cards
}
mutating func add(card: Card) {
self.cards.append(card)
}
}
The design goals are:
cards property is not modifiable outside of the class so its type should be List<Card>
fun add(card: Card) should modify the internal cards list
Is there a way to achieve this in Kotlin without using two separate properties - one private var mutableCards: MutableList<Card> and one computed property val cards: List<Card> get() = this.mutableCards
I need some best practice for such situation.
Since the read-only List is "under the hood" also a mutable list, you may want to leverage on casting to a MutableList, doing so:
class Card {
}
class Deck(cards:List<Card>){
var cards:List<Card>
init {
this.cards = cards
}
public fun add(card:Card) {
(cards as MutableList<Card>).add(card)
}
}
fun main(args: Array<String>) {
var cards:List<Card> = arrayListOf()
// here I can't modify cards
var deck = Deck(cards)
deck.add(Card())
print(deck.cards.count()) // printing 1
}
to be able to test it, copy and paste here.
I have an array of CGPoints (spritePositions) and I would like to create SKSpriteNode's with a selected number of positions (leaving specific indexes of the out). Please see code below:
CreateSprite(missingIndexes: [int]) {
//for (index, value) in enumerate(spritePositions) filtering out/excluding missingIndexes array {
var sprite = SKSpriteNode(imageNamed: "spriteImage")
sprite.position = value
addChild(sprite)
}
}
You can use the contains function on the missingIndexes array to filter out your indices. If the index is not contained in your missingIndexes process as normal. If the index is containted in missingIndexes, do nothing.
Swift 1.2:
for (index, value) in enumerate(spritePositions) {
if !contains(missingIndexes, index) {
var sprite = SKSpriteNode(imageNamed: "spriteImage")
sprite.position = value
addChild(sprite)
}
}
Swift 2.0
for (index, value) in spritePositions.enumerate() {
if !missingIndexes.contains(index) {
var sprite = SKSpriteNode(imageNamed: "spriteImage")
sprite.position = value
addChild(sprite)
}
}
Swift 1.2:
var filteredObjects = spritePositions.filter { !contains(missingIndexes, find(spritePositions, $0)!)}
gets a list by filtering the indices
for object in filteredObjects {
print(object)
}
uses that in a loop
Not necessarily the best way to do it, just a way using filter, which will allow you to keep the filtered array if you need it later.
Yet another option would be to use a combination of the Set type and the map function. The below snippet is for Swift 1.2.
let missingIndexes: [Int] = ...
let spritePositions: [CGPoint] = ...
map(Set(0 ..< count(spritePositions)).subtract(missingIndexes), { index -> Void in
var sprite = SKSpriteNode(imageNamed: "spriteImage")
sprite.position = spritePositions[index]
addChild(sprite)
})
This is more efficient than repeatedly calling contains on the missingIndexes array.
I'm getting a: Cannot invoke 'append' with an argument list of type '([Book])' It works find if I use the += but I don't understand why append() won't work.
struct Book
{
var title:String
var pageCount:Int
}
class Library
{
var onShelfBooks:[Book] = []
var onLoanBooks:[Book] = []
var books:[Book]
{
get
{
return onShelfBooks + onLoanBooks
}
set(newBook)
{
onShelfBooks.append(newBook)
}
}
}
struct Book
{
var title:String
var pageCount:Int
}
class Library
{
var onShelfBooks:[Book] = []
var onLoanBooks:[Book] = []
var books:[Book]
{
get
{
return onShelfBooks + onLoanBooks
}
set(newBook)
{
onShelfBooks.append(newBook[0])
}
}
}
var myLibrary = Library()
var newBook = Book(title: "Swift Development with Cocoa", pageCount: 453)
myLibrary.books = [newBook]
myLibrary.books
Append only allows you to add one object at a time while += allows you to combine an array of objects with another object. When you call append on the setter you are trying to add an array of book objects, or [Book] instead of just a single book object.
If you would like to add [newBook] with append, you can use : of
1- onShelfBooks.append(contentsOf: newBook)
"contentOf" is type of Sequence.
otherwise use of:
2- onShelfBooks += newBook
How do i make 1 array that has the keys of 1st array & its values are the values of the 2nd array in Actionscript 3.0?
Below is my [WRONG] code. Package import left out.
public class myPages extends Sprite {
protected var pageNames:Array = [];
protected var pageLayoutNo:Array = [];
private var pageLayoutNames:Object = new Object();
public function assignNamesLayouts {
//all the names of the pages
for(i=0; i<totalPages; i++) {
var pageMc:MovieClip=new MovieClip();
pageMc.name = menu.config.pages.page[i].#name;
pageNames[i]= pageMc.name;
}
//all layout numbers
for(i=0; i<totalPages; i++) {
pageLayoutNo[i] = menu.config.layoutNum.layNum[i];
}
setNames(pageNames);
setPLNo(pageLayoutNo);
setLayoutNames(pageLayoutNo,pageNames);
}
protected function setNames(a:Array) {
pageNames = a;
}
protected function setPLNo(a:Array) {
pageLayoutNo = a;
}
protected function setLayoutNames(a:Array, b:Array) {
var maps:Object = new Object();
maps.no = a;
maps.nm = b;
for each(var k:int in a) {
maps[k] = b;
}
}
}
Thank you.
I will agree with #Vesper. Closest solution to the associative array will be Dictionary. I also will add, some ideas for you. If your collection will have keys as Strings, you could use simple Object:
var keyName: String = "key1";
var anotherKey: String = "key2";
var collection: Object = {};
collection[keyName] = new MovieClip();
collection[anotherKey] = new Sprite();
trace(collection[keyName]);
If you want to use 2 arrays: one for keys, another one for values, you will need several functions(place object with key, get object by key, get object by value, remove object by key, etc.) to manage them, because indexes in arrays must be identical.
Dictionary is really what you want, but you could do it by creating an array of objects like this:
var newArray=combineArrays("name", arr1,"number",arr2);
function combineArrays(name1, arr1,name2, arr2){
var newArr=new Array();
for(var i<arr1){
var obj=new Object();
obj[name1]=arr1[i];
obj[name2]=arr2[i];
newArr.push(obj);
}
}
Then you can search on the object names or values for either object.