Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swap array values at specific indices?

I'm creating asynchronous NSURLConnections for images based off of an array of dictionaries, each with their own image URL:

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

Given the asynchronous nature of the connections (which is what I want, fastest images load first), the images might load in a different order, such as:

url0
url2
url3
url1

If the images are loaded out of order, however, the original posts array needs to be reorganized according to when the images loaded. So, given the above example, posts should now look like:

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

Is there any way in Swift to swap values of an array at a specific index with values from the same array at a different index? I first attempted this by using the swap function:

// 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])
    }
}

I then attempted something similar without the swap function:

// 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 

In both scenarios, however, the array values are not correctly swapped. I realize this is a logic error, but I'm failing to see where the error actually lies.

As far as I can tell, it's swapping correctly the first time, but then the new dictionary has an incorrect calledIndex value, causing it swap back to its original position.

This hypothesis might be completely wrong, and I realize I'm having a difficult time describing the situation, but I will attempt to provide as much clarification as possible.


I made a test case, you can download the source code here. The code for it is:

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
}

The output is:

Loaded: 1 0 2 3

Actual: 0 1 2 3

However the expected, "Actual" output should be:

1 0 2 3

It's worth noting that using the tuples code results in slightly wonky results, but nothing that matches the actual order. You can see what I mean by uncommenting that line.

like image 788
Charlie Avatar asked Jul 23 '15 19:07

Charlie


People also ask

How do you swap values in an array?

The built-in swap() function can swap two values in an array . template <class T> void swap (T& a, T& b); The swap() function takes two arguments of any data type, i.e., the two values that need to be swapped.

How do you swap the contents of an array explain?

To swap elements of two arrays you have to swap each pair of elemenets separatly. And you have to supply the number of elements in the arrays. Otherwise the arrays need to have a sentinel value. Here is a demonstrative program that shows how the function swap can be defined.

How do you change the index of an element in an array?

To change the position of an element in an array:Use the splice() method to insert the element at the new index in the array. The splice method changes the original array by removing or replacing existing elements, or adding new elements at a specific index.

Can we change the array index?

Array indexing starts at zero in C; you cannot change that.


Video Answer


4 Answers

You can just assign via tuples:

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

But what problem are you actually having with swap? The following should work fine:

swap(&xs[1], &xs[2])
like image 59
Rob Napier Avatar answered Oct 31 '22 23:10

Rob Napier


If you can change the changeIndex's value type to String, then below code should work

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"]
})
like image 36
neo Avatar answered Oct 31 '22 23:10

neo


You can just use sort on the calledIndex value:

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)}
like image 44
Gwendle Avatar answered Nov 01 '22 01:11

Gwendle


The problem is that swapping the values doesn't give you the right order, as you may swap an already swapped value. As an example, if you receive in the order 3,0,1,2, then you'll swap:

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

So this will give you 1,2,0,3 even though you received (and correctly swapped) 3,0,1,2.

If you want the swapping to work you'll have to keep track of what's been swapped so you know which index to swap into. It's probably going to be easier to add to a new array as you get the data back, or to add a new field to storing the loaded index and sort on that at the very end.

like image 34
MattM Avatar answered Nov 01 '22 00:11

MattM