2
var allEvents: [String] = ["SOUTHERN STATES PASS LAWS TO DISENFRANCISE BLACKS", "THEODORE ROOSEVELT ELECTED VICE PRESIDENT", "WRIGHT BROTHERS FIRST FLIGHT", "SAN FRANCISCO EARTHQUAKE", "FORD INTRODUCES MODEL T", "WORLD WAR I BEGINS IN EUROPE", "WORLD WAR I ENDS", "TR DIES – CARNEGIE DIES", "GERMAN MONEY HYPER INFLATES", "TREATY OF VERSAILLES, 19TH AMENDMENT", "HINDENBURG ELECTED PRESIDENT OF GERMANY", "STOCK MARKET CRASHES, DEPRESSION BEGINS", "HITLER’S NAZI PARTY GAINS MAJORITY IN PARLIAMENT", "HITLER BEGINS TO TAKE POWER IN GERMANY", "FRANKLIN ROOSEVELT ELECTED PRESIDENT", "FDR INAUGURATED “100 DAYS”", "HINDENBURG DIES, HITLER APPOINTS HIMSELF PRESIDENT  -  “ FUEHRER”", "FDR RE-ELECTED", "German airship HINDENBURG BURNS IN NEW JERSEY", "“MUNICH” – British P.M. Chamberlain agrees that Hitler can have Czechoslovakia.", "Germany invades Holland, Belgium and France – Churchill becomes P.M.", "Germany invades Russia", "JAPANESE ATTACK PEARL HARBOR – U.S. DECLARES WAR ON JAPAN", "HITLER DECLARES WAR ON U.S."]

var eventIndex1: Int = 0
var eventIndex2: Int = 0
var eventIndex3: Int = 0
var eventIndex4: Int = 0

func generateRandomIndex() -> [Int] {
    while (eventIndex1 == eventIndex2) && (eventIndex2 == eventIndex3) && (eventIndex1 == eventIndex3) && (eventIndex1 == eventIndex4) && (eventIndex2 == eventIndex4) && (eventIndex3 == eventIndex4) {
        eventIndex1 = GKRandomSource.sharedRandom().nextInt(upperBound: allEvents.count)
        eventIndex2 = GKRandomSource.sharedRandom().nextInt(upperBound: allEvents.count)
        eventIndex3 = GKRandomSource.sharedRandom().nextInt(upperBound: allEvents.count)
        eventIndex4 = GKRandomSource.sharedRandom().nextInt(upperBound: allEvents.count)
    }
    return [eventIndex1, eventIndex2, eventIndex3, eventIndex4]
}

var eventIndexes = generateRandomIndex()
var index1 = eventIndexes[0]
var index2 = eventIndexes[1]
var index3 = eventIndexes[2]
var index4 = eventIndexes[3]
var event1 = allEvents[index1]
var event2 = allEvents[index2]
var event3 = allEvents[index3]
var event4 = allEvents[index4]

The following code above basically pulls from the allEvents array using the no-duplicate random indexes that the function is supposed to generate. Besides each event variable, there is a ‘Cannot use instance member ‘allEvents’ within property initializers; property initializers run before ‘self’ is available’. Any suggestions? No idea how to fix.

  • Your `generateRandomIndex()` function doesn't do what you think it does. It doesn't promise a lack of duplicates (you used `&&` when you meant `||`). The standard way to do what you're trying to do is called a shuffle. See http://stackoverflow.com/questions/24026510/how-do-i-shuffle-an-array-in-swift – Rob Napier Dec 12 '16 at 14:28
  • 2
    Your statement "global scope" confused Eric (you didn't mean "global scope," you meant that these are properties of the ViewController). – Rob Napier Dec 12 '16 at 14:30
  • @RobNapier, I'm a little bit new to code, so I don't quite understand the syntax thats used to create a shuffle method. It would be much more simple if i could just use more basic syntax to convey my logic. So whats wrong with using &&? The while loop keeps generating random numbers until ALL (hence, &&) event indexes are not identical to one each other. Using || would destroy this logic. – Ammanuel Roberts Dec 12 '16 at 14:32
  • @RobNapier Oh yes, I meant that this is within the ViewController class – Ammanuel Roberts Dec 12 '16 at 14:33
  • Work through a couple of examples and you'll see what I mean. Consider the case where the values are 0,1,1,1 and see whether it loops again. – Rob Napier Dec 12 '16 at 14:35
  • @RobNapier I'm not sure if this is the solution to my problem. My problem isn't generating a random index; I've done that. Nor is it checking whether the random indexes are the same; I've done that. Its just this random problem that popped up. That i have no idea why it popped up: ‘Cannot use instance member ‘allEvents’ within property initializers; property initializers run before ‘self’ is available – Ammanuel Roberts Dec 12 '16 at 14:48
  • Shorten your list (start with a 4 element list) and test it; your logic creates duplicates. You're just getting lucky because there are so many entries and you ask for so few of them (so duplicates are possible but more rare). If your list were shorter, you'd see duplicates all the time out of `generateRandomIndex()`. – Rob Napier Dec 12 '16 at 14:57

1 Answers1

2

You can't initialize a standard property with the value of another property (and there's no promise that they'll be initialized in source code order the way you're assuming they will be). You can initialize a lazy property with the value of another one, but that's way too complicated for this problem. Accessing an object's property requires that the entire object be initialized, and it isn't initialized until all the properties are set.

This kind of logic is best done in an init. Here's how I would write it myself (with the shuffle extension):

class ViewController {
    //There's just one of these in the world, not one per class. And I can't
    // imagine it changing (so should be let)
    static let allEvents = ["SOUTHERN STATES PASS LAWS TO DISENFRANCISE BLACKS", "THEODORE ROOSEVELT ELECTED VICE PRESIDENT", "WRIGHT BROTHERS FIRST FLIGHT", "SAN FRANCISCO EARTHQUAKE", "FORD INTRODUCES MODEL T", "WORLD WAR I BEGINS IN EUROPE", "WORLD WAR I ENDS", "TR DIES – CARNEGIE DIES", "GERMAN MONEY HYPER INFLATES", "TREATY OF VERSAILLES, 19TH AMENDMENT", "HINDENBURG ELECTED PRESIDENT OF GERMANY", "STOCK MARKET CRASHES, DEPRESSION BEGINS", "HITLER’S NAZI PARTY GAINS MAJORITY IN PARLIAMENT", "HITLER BEGINS TO TAKE POWER IN GERMANY", "FRANKLIN ROOSEVELT ELECTED PRESIDENT", "FDR INAUGURATED “100 DAYS”", "HINDENBURG DIES, HITLER APPOINTS HIMSELF PRESIDENT  -  “ FUEHRER”", "FDR RE-ELECTED", "German airship HINDENBURG BURNS IN NEW JERSEY", "“MUNICH” – British P.M. Chamberlain agrees that Hitler can have Czechoslovakia.", "Germany invades Holland, Belgium and France – Churchill becomes P.M.", "Germany invades Russia", "JAPANESE ATTACK PEARL HARBOR – U.S. DECLARES WAR ON JAPAN", "HITLER DECLARES WAR ON U.S."]

    // Do you really need both of these? If so, I'd probably actually just keep
    // eventIndices and compute events when needed, but if you access it a lot,
    // this is fine. But "event1" "event2" etc is bad practice
    let eventIndices: [Int]
    let events: [String]

    init() {
        eventIndices = Array(ViewController.allEvents.indices.shuffled().prefix(4))
        events = eventIndices.map { ViewController.allEvents[$0] }
    }
}

OK, that's how I'd do it, but if we want to keep it in the style you have, we can still do that. You'll still need allEvents to be static though.

class ViewController {

    static let allEvents: [String] = ["SOUTHERN STATES PASS LAWS TO DISENFRANCISE BLACKS", "THEODORE ROOSEVELT ELECTED VICE PRESIDENT", "WRIGHT BROTHERS FIRST FLIGHT", "SAN FRANCISCO EARTHQUAKE", "FORD INTRODUCES MODEL T", "WORLD WAR I BEGINS IN EUROPE", "WORLD WAR I ENDS", "TR DIES – CARNEGIE DIES", "GERMAN MONEY HYPER INFLATES", "TREATY OF VERSAILLES, 19TH AMENDMENT", "HINDENBURG ELECTED PRESIDENT OF GERMANY", "STOCK MARKET CRASHES, DEPRESSION BEGINS", "HITLER’S NAZI PARTY GAINS MAJORITY IN PARLIAMENT", "HITLER BEGINS TO TAKE POWER IN GERMANY", "FRANKLIN ROOSEVELT ELECTED PRESIDENT", "FDR INAUGURATED “100 DAYS”", "HINDENBURG DIES, HITLER APPOINTS HIMSELF PRESIDENT  -  “ FUEHRER”", "FDR RE-ELECTED", "German airship HINDENBURG BURNS IN NEW JERSEY", "“MUNICH” – British P.M. Chamberlain agrees that Hitler can have Czechoslovakia.", "Germany invades Holland, Belgium and France – Churchill becomes P.M.", "Germany invades Russia", "JAPANESE ATTACK PEARL HARBOR – U.S. DECLARES WAR ON JAPAN", "HITLER DECLARES WAR ON U.S."]

    static func generateRandomIndex() -> [Int] {
        var eventIndex1: Int = 0
        var eventIndex2: Int = 0
        var eventIndex3: Int = 0
        var eventIndex4: Int = 0

        while (eventIndex1 == eventIndex2) || (eventIndex2 == eventIndex3) || (eventIndex1 == eventIndex3) || (eventIndex1 == eventIndex4) || (eventIndex2 == eventIndex4) || (eventIndex3 == eventIndex4) {
            eventIndex1 = GKRandomSource.sharedRandom().nextInt(upperBound: allEvents.count)
            eventIndex2 = GKRandomSource.sharedRandom().nextInt(upperBound: allEvents.count)
            eventIndex3 = GKRandomSource.sharedRandom().nextInt(upperBound: allEvents.count)
            eventIndex4 = GKRandomSource.sharedRandom().nextInt(upperBound: allEvents.count)
        }
        return [eventIndex1, eventIndex2, eventIndex3, eventIndex4]
    }

    let index1: Int
    let index2: Int
    let index3: Int
    let index4: Int

    let event1: String
    let event2: String
    let event3: String
    let event4: String

    init() {
        let eventIndexes = ViewController.generateRandomIndex()
        index1 = eventIndexes[0]
        index2 = eventIndexes[1]
        index3 = eventIndexes[2]
        index4 = eventIndexes[3]
        event1 = ViewController.allEvents[index1]
        event2 = ViewController.allEvents[index2]
        event3 = ViewController.allEvents[index3]
        event4 = ViewController.allEvents[index4]
    }
}

If allEvents doesn't need to be seen outside this file, you can make it simpler by hoisting it to file scope:

fileprivate let allEvents: [String] = ["SOUTHERN STATES PASS LAWS TO DISENFRANCISE BLACKS", "THEODORE ROOSEVELT ELECTED VICE PRESIDENT", "WRIGHT BROTHERS FIRST FLIGHT", "SAN FRANCISCO EARTHQUAKE", "FORD INTRODUCES MODEL T", "WORLD WAR I BEGINS IN EUROPE", "WORLD WAR I ENDS", "TR DIES – CARNEGIE DIES", "GERMAN MONEY HYPER INFLATES", "TREATY OF VERSAILLES, 19TH AMENDMENT", "HINDENBURG ELECTED PRESIDENT OF GERMANY", "STOCK MARKET CRASHES, DEPRESSION BEGINS", "HITLER’S NAZI PARTY GAINS MAJORITY IN PARLIAMENT", "HITLER BEGINS TO TAKE POWER IN GERMANY", "FRANKLIN ROOSEVELT ELECTED PRESIDENT", "FDR INAUGURATED “100 DAYS”", "HINDENBURG DIES, HITLER APPOINTS HIMSELF PRESIDENT  -  “ FUEHRER”", "FDR RE-ELECTED", "German airship HINDENBURG BURNS IN NEW JERSEY", "“MUNICH” – British P.M. Chamberlain agrees that Hitler can have Czechoslovakia.", "Germany invades Holland, Belgium and France – Churchill becomes P.M.", "Germany invades Russia", "JAPANESE ATTACK PEARL HARBOR – U.S. DECLARES WAR ON JAPAN", "HITLER DECLARES WAR ON U.S."]

class ViewController {
    ...
        event1 = allEvents[index1]
        event2 = allEvents[index2]
        event3 = allEvents[index3]
        event4 = allEvents[index4]
    ...

But the key lesson in all this is that you can't access properties in an object before the object is initialized.

Community
  • 1
  • 1
Rob Napier
  • 286,113
  • 34
  • 456
  • 610