在特定索引处交换数组值?

时间:2015-07-23 19:07:41

标签: arrays swift cocoa

我根据字典数组为图像创建异步NSURLConnections,每个字典都有自己的图片网址:

var posts = [
    ["url": "url0", "calledIndex": 0],
    ["url": "url1", "calledIndex": 1],
    ["url": "url2", "calledIndex": 2],
    ["url": "url3", "calledIndex": 3]
]

鉴于连接的异步性质(这是我想要的,最快的图像首先加载),图像可能以不同的顺序加载,例如:

url0
url2
url3
url1

但是,如果图像无序加载,则需要根据加载的图像重新组织原始posts数组。因此,鉴于上面的示例,posts现在应该如下所示:

var posts = [
    ["url": "url0", "calledIndex": 0],
    ["url": "url2", "calledIndex": 2],
    ["url": "url3", "calledIndex": 3],
    ["url": "url1", "calledIndex": 1]
]

在Swift中是否有任何方法可以将特定索引处的数组值与来自不同索引的同一数组的值交换?我首先尝试使用swap函数:

// Index the images load
var loadedIndex = 0

func connectionDidFinishLoading(connection: NSURLConnection) {

    // Index of the called image in posts
    let calledIndex = posts["calledIndex"] as! Int

    // Index that the image actually loaded
    let loadedIndex = loadedIndex

    // If the indicies are the same, the image is already in the correct position
    if loadedIndex != calledIndex {

        // If they're not the same, swap them
        swap(&posts[calledIndex], &posts[loadedIndex])
    }
}

然后我在没有swap函数的情况下尝试了类似的东西:

// The post that was actually loaded
let loadedPost = posts[calledIndex]

// The post at the correct index
let postAtCorrectIndex = posts[loadedIndex]

posts[calledIndex] = postAtCorrectIndex
posts[loadedIndex] = loadedPost 

但是,在这两种情况下,都没有正确交换数组值。我意识到这是一个逻辑错误,但我没有看到错误究竟在哪里。

据我所知,它第一次正确交换,但是新词典的calledIndex值不正确,导致它换回原来的位置。

这个假设可能是完全错误的,我意识到我很难描述这种情况,但我会尝试提供尽可能多的澄清。

我做了一个测试用例,你可以download the source code here。它的代码是:

var allPosts:Array<Dictionary<String, AnyObject>> = [
    ["imageURL": "http://i.imgur.com/aLsnGqn.jpg", "postTitle":"0"],
    ["imageURL": "http://i.imgur.com/vgTXEYY.png", "postTitle":"1"],
    ["imageURL": "http://i.imgur.com/OXzDEA6.jpg", "postTitle":"2"],
    ["imageURL": "http://i.imgur.com/ilOKOx5.jpg", "postTitle":"3"],
]

var lastIndex = 0
var threshold = 4
var activeConnections = Dictionary<NSURLConnection, Dictionary<String, AnyObject?>>()

func loadBatchInForwardDirection(){
    func createConnection(i: Int){
        allPosts[i]["calledIndex"] = i
        var post = allPosts[i]
        let imageURL = NSURL(string: post["imageURL"] as! String)
        if imageURL != nil {
            let request = NSMutableURLRequest(URL: imageURL!, cachePolicy: .ReloadIgnoringLocalCacheData, timeoutInterval: 60)
            let connection = NSURLConnection(request: request, delegate: self, startImmediately: true)
            if connection != nil {
                activeConnections[connection!] = post
            }
        }
    }
    let startingIndex = lastIndex;
    for (var i = startingIndex; i < startingIndex + threshold; i++){
        createConnection(i)
        lastIndex++
    }
}

func connection(connection: NSURLConnection, didReceiveData data: NSData) {
    if activeConnections[connection] != nil {
        let dataDict = activeConnections[connection]!["data"]
        if dataDict == nil {
            activeConnections[connection]!["data"] = NSMutableData(data: data)
        } else {
            (activeConnections[connection]!["data"] as! NSMutableData).appendData(data)
        }
    }
}

var loadedIndex = 0
func connectionDidFinishLoading(connection: NSURLConnection) {
    let loadedPost = activeConnections[connection]!
    activeConnections.removeValueForKey(connection)
    let data = loadedPost["data"] as? NSData
    let calledIndex = loadedPost["calledIndex"] as! Int
    println(calledIndex)

    swap(&allPosts[calledIndex], &allPosts[loadedIndex])
    //(allPosts[calledIndex], allPosts[loadedIndex]) = (allPosts[loadedIndex], allPosts[calledIndex])

    loadedIndex++
    done(loadedIndex)
}

func done(index: Int){
    if index == 4 {
        println()
        println("Actual: ")
        println(allPosts[0]["postTitle"] as! String)
        println(allPosts[1]["postTitle"] as! String)
        println(allPosts[2]["postTitle"] as! String)
        println(allPosts[3]["postTitle"] as! String)
    }
}

func applicationDidFinishLaunching(aNotification: NSNotification) {
    loadBatchInForwardDirection()
    println("Loaded: ")
}

func applicationWillTerminate(aNotification: NSNotification) {
    // Insert code here to tear down your application
}

输出结果为:

  

加载:   1   0   2   3

     

实际值:   0   1   2   3

然而,预期,&#34;实际&#34;输出应该是:

  

1 0 2 3

值得注意的是,使用元组代码会导致稍微不稳定的结果,但没有任何与实际顺序匹配的结果。您可以通过取消注释该行来了解我的意思。

6 个答案:

答案 0 :(得分:14)

您可以通过元组分配:

var xs = [1,2,3]   
(xs[1], xs[2]) = (xs[2], xs[1])

但是你对swap实际上遇到了什么问题?以下应该可以正常工作:

swap(&xs[1], &xs[2])

答案 1 :(得分:3)

如果你可以将changeIndex的值类型更改为String,那么下面的代码应该可以工作

var posts = [
    ["url": "url0", "calledIndex": "0"],
    ["url": "url2", "calledIndex": "2"],
    ["url": "url3", "calledIndex": "3"],
    ["url": "url1", "calledIndex": "1"]
]

posts = sorted(posts, { (s1: [String:String], s2: [String:String]) -> Bool in
    return s1["calledIndex"] < s2["calledIndex"]
})

答案 2 :(得分:2)

您可以对calledIndex值使用sort:

var posts = [
  ["url": "url0", "calledIndex": 0],
  ["url": "url2", "calledIndex": 2],
  ["url": "url1", "calledIndex": 1],
  ["url": "url3", "calledIndex": 3]
]


var sortedPosts = sorted(posts) { ($0["calledIndex"] as! Int) < ($1["calledIndex"] as! Int)}

答案 3 :(得分:2)

问题是交换值不能为您提供正确的顺序,因为您可以交换已经交换的值。例如,如果您收到订单3,0,1,2,那么您将交换:

array_before   called_index   loaded_index   array_after
0, 1, 2, 3     0              3              3, 1, 2, 0
3, 1, 2, 0     1              0              1, 3, 2, 0
1, 3, 2, 0     2              1              1, 2, 3, 0
1, 2, 3, 0     3              2              1, 2, 0, 3

所以即使你收到(并且正确交换)3,0,1,2,这也会给你1,2,0,3。

如果您希望交换工作,您必须跟踪已交换的内容,以便了解要交换的索引。当你获得数据时,可能会更容易添加到新数组,或者添加一个新字段来存储加载的索引并在最后对其进行排序。

答案 4 :(得分:2)

这实际上最终比预期容易得多。由于当前加载的帖子保存在activeConnections中,我只需将加载索引处的allPosts值替换为当前加载的帖子:

allPosts[loadedIndex] = loadedPost

我无法想到这种方法的任何缺点,但如果有人想到,请告诉我。

答案 5 :(得分:0)

在Swift 4中

  array.swapAt(i, j)  // will swap elements at i and j

协议

protocol MutableCollection {
  /// Exchange the values at indices `i` and `j`.
  ///
  /// Has no effect when `i` and `j` are equal.
  public mutating func swapAt(_ i: Index, _ j: Index)
}

有关swapAt的详细信息,请参阅SE-0173 Add MutableCollection.swapAt(::)