web-dev-qa-db-ja.com

迅速:辞書内の配列の変更

辞書内の配列に要素を簡単に追加するにはどうすればよいですか?常にcould not find member 'append'またはcould not find an overload for '+='

var dict = Dictionary<String, Array<Int>>()
dict["key"] = [1, 2, 3]

// all of these fail
dict["key"] += 4
dict["key"].append(4) // xcode suggests dict["key"].?.append(4) which also fails
dict["key"]!.append(4)
dict["key"]?.append(4)

// however, I can do this:
var arr = dict["key"]!
arr.append(4) // this alone doesn't affect dict because it's a value type (and was copied)
dict["key"] = arr

配列を変数に割り当てるだけで、それを変更してからディクショナリに再割り当てすると、すべてをコピーしませんか?それは効率的でもエレガントでもありません。

27
maltalef

Swiftベータ5にはこの機能が追加されており、2、3回の試行で新しいメソッドが追加されました。ラップ解除演算子!および?は、値を演算子またはメソッド呼び出しのいずれかに渡すようになりました。つまり、次のいずれかの方法でその配列に追加できます。

dict["key"]! += [4]
dict["key"]!.append(4)
dict["key"]?.append(4)

いつものように、使用する演算子に注意してください。辞書にない値を強制的にアンラップすると、実行時エラーが発生します。

dict["no-key"]! += [5]        // CRASH!

オプションの連鎖を使用すると、サイレントモードで失敗します。

dict["no-key"]?.append(5)     // Did it work? Swift won't tell you...

理想的には、新しいヌル合体演算子??を使用してこの2番目のケースに対処できるはずですが、現在は機能していません。


Swift以前のベータ5からの回答:

これはSwiftがやろうとしていることを実行できないということです。問題はvalue任意のオプション変数は、実際には定数です-強制的にアンラップする場合でも、オプション配列を定義する場合、次のことができます。

var arr: Array<Int>? = [1, 2, 3]
arr[0] = 5
// doesn't work: you can't subscript an optional variable
arr![0] = 5
// doesn't work: constant arrays don't allow changing contents
arr += 4
// doesn't work: you can't append to an optional variable
arr! += 4
arr!.append(4)
// these don't work: constant arrays can't have their length changed

辞書で問題が発生する理由は、辞書にそのキーがあるという保証がないため、辞書に添え字を付けるとOptional値が返されるためです。したがって、ディクショナリ内の配列の動作は、上記のオプションの配列と同じです。

var dict = Dictionary<String, Array<Int>>()
dict["key"] = [1, 2, 3]
dict["key"][0] = 5         // doesn't work
dict["key"]![0] = 5        // doesn't work
dict["key"] += 4           // uh uh
dict["key"]! += 4          // still no
dict["key"]!.append(4)     // nope

辞書内の配列内の何かを変更する必要がある場合は、次のように配列のコピーを取得して変更し、再割り当てする必要があります。

if var arr = dict["key"] {
    arr.append(4)
    dict["key"] = arr
}

ETA:Swift beta 3でも同じ手法が機能しますが、定数配列では内容を変更できなくなります。

31
Nate Cook

簡単な回避策として、NSMutableArrayを使用できます。

import Foundation

var dict = Dictionary<String, NSMutableArray>()
dict["key"] = [1, 2, 3] as NSMutableArray
dict["key"]!.addObject(4)

私は私のプロジェクトでこのようなシンプルなソリューションを効果的に使用しています:

https://github.com/gui-dos/Guigna/blob/5c02f7e70c8ee3b2265f6916c6cbbe5cd3963fb5/Guigna-Swift/Guigna/GuignaAppDelegate.Swift#L1150-L1157

6
gui_dos

受け入れられた答えは、次のはるかに単純な可能性をバイパスします。これは、古いSwift=バージョンでも機能します:

var dict = Dictionary<String, Array<Int>>()
dict["key"] = [1, 2, 3]

print(dict)

dict["key", default: [Int]()].append(4)

print(dict)

これは印刷されます:

["key": [1, 2, 3]]
["key": [1, 2, 3, 4]]

この:

var dict = Dictionary<String, Array<Int>>()
dict["key", default: [Int]()].append(4)
print(dict)

印刷されます:

["key": [4]]
2

彼の質の高い答えに対するコメントの中で、私がネイト・クックに言っていたことがここにあります。これは、「辞書内の配列に要素を簡単に追加する」と考えるものです。

dict["key"] = dict["key"]! + 4
dict["key"] = dict["key"] ? dict["key"]! + 4 : [4]

今のところ、+演算子を自分で定義する必要があります。

@infix func +<T>(array: T[], element: T) -> T[] {
    var copy = array
    copy += element
    return copy
}

このバージョンでは安全性が大幅に低下すると思います。多分複合演算子で定義しますか?

@infix func +<T>(array: T[]?, element: T) -> T[] {
    return array ? array! + element : [element]
}

dict["key"] = dict["key"] + 4

最後に、これは私が取得できる最もクリーンなものですが、この例では配列の値/参照がどのように機能するかについて混乱しています。

@assignment func +=<T>(inout array: T[]?, element: T) {
    array = array + element
}

dict["key"] += 5
1
Jessy