在Swift中,我如何仅从.json文件加载或使用过滤数据

时间:2022-06-02 05:58:08

Ok, so I've been learning how to program in my spare time over the past couple of months and I'm now trying to get my head around how to only use data from a .json file that has been filtered.

好的,所以我在过去的几个月里一直在学习如何在业余时间编程,现在我正试图了解如何仅使用已过滤的.json文件中的数据。

For example, let's say I have a .json file in the following format:

例如,假设我有一个.json文件,格式如下:

    {
        "id" : "1",
        "question": "Earth is a:",
             "answers": [
            "Planet",
            "Meteor",
            "Star",
            "Asteroid"
          ],
        "category": "astronomy"
    },
    {
        "id":"2",
        "question":"Which country won the 2006 Soccer World Cup?",
        "answers":[
            "Italy",
            "Germany",
            "France",
            "Brazil"
            ],
        "category": "sport"
 }

My Question

我的问题

Assuming I have all my data in a single .json file, how do I load only the data that meets a certain criteria, or load all the data but then only select the data that meets a certain criteria? Obviously the end result must be that only the filtered data is shuffled and displayed to users.

假设我将所有数据都放在一个.json文件中,如何仅加载满足特定条件的数据,或者加载所有数据,然后只选择符合特定条件的数据?显然,最终结果必须是只将过滤后的数据混洗并显示给用户。

Apologies if this is obvious, but as I'm a newbie I just don't have the knowledge/experience to find a better way to do this than the multiple files approach I describe below. I did try searching for an answer, but couldn't find one that seems to apply to my scenario. If there is and I've missed it, please refer me to that instead.

抱歉,如果这是显而易见的,但由于我是新手,我只是没有知识/经验来找到比我下面描述的多文件方法更好的方法。我确实尝试寻找答案,但找不到一个似乎适用于我的场景。如果有,我错过了,请转发给我。

Some context

一些背景

At present I'm using the following code to load my data:

目前我正在使用以下代码加载我的数据:

  func LoadAllQuestionsAndAnswers()
    {
        let path = NSBundle.mainBundle().pathForResource("content", ofType: "json")
        let jsonData : NSData = NSData(contentsOfFile: path!)!
        allEntries = (try! NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers)) as! NSArray
        //println(allEntries)

    }

Obviously, the above loads all of the data from the .json file. Once this data is loaded I then shuffle all the questions using the following code:

显然,上面加载了.json文件中的所有数据。加载此数据后,我将使用以下代码重新调整所有问题:

if #available(iOS 9.0, *) {
            shuffledQuestions = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(allEntries as [AnyObject])
            nextQuestion++
            LoadQuestion(nextQuestion)

            // Fallback on earlier versions
        }else{

            let randomNumber = Int(arc4random_uniform(UInt32(allEntries.count)))
            LoadQuestionPreiOS9(randomNumber)

        }

Once shuffled I then present the question to users with the following code:

一旦洗牌,我就会使用以下代码向用户提出问题:

func LoadQuestion(index : Int)
{
    let entry : NSDictionary = shuffledQuestions[index] as! NSDictionary
    let question : NSString = entry.objectForKey("question") as! NSString
    let arr : NSMutableArray = entry.objectForKey("answers") as! NSMutableArray

    let QID : NSString = entry.objectForKey("id") as! NSString

    if let a = arr.objectAtIndex(0) as? String {
        self.answertext = a
    }

    //println(question)
    //println(arr)

    labelQuestion.text = question as String
    labelQID.text = QID as String

So, what I would like to do, is give users the option to select the category of questions they want to answer. Now obviously I could do this by using separate .json files for each category and using if else statements. For example, I could have an astronomy.json file, a sport.json file, etc and then use code such as:

所以,我想做的是让用户选择他们想要回答的问题类别。现在显然我可以通过为每个类别使用单独的.json文件并使用if else语句来完成此操作。例如,我可以有一个astronomy.json文件,一个sport.json文件等,然后使用如下代码:

    func LoadChosenCategory()
    {
        let defaults = NSUserDefaults.standardUserDefaults()

        if (chosencategory == 1)
        {
let path = NSBundle.mainBundle().pathForResource("astronomy", ofType: "json")
        let jsonData : NSData = NSData(contentsOfFile: path!)!
        allEntries = (try! NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers)) as! NSArray
        //println(allEntries)

        }else{

            if (lastchosenchallenge == 2)
            {
let path = NSBundle.mainBundle().pathForResource("sport", ofType: "json")
        let jsonData : NSData = NSData(contentsOfFile: path!)!
        allEntries = (try! NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers)) as! NSArray
        //println(allEntries)

            }else{

And so on and so on.

等等等等。

But I am sure there must be more efficient ways to do this and I would like to learn how, hence this question.

但我相信必须有更有效的方法来做到这一点,我想学习如何,因此这个问题。


Ok, so I've tried to follow the advice in Russell's answer, but just can't get it to work. It runs fine, but just doesn't seem to actually be filtering the data.

好的,所以我试着按照Russell的回答中的建议,但是无法让它发挥作用。它运行正常,但似乎并没有真正过滤数据。

Below is what I've done to try and implement Russell's answer.

以下是我尝试实施Russell的答案。

First I created a couple of integer variables:

首先,我创建了几个整数变量:

var categoryfilter : Int! = 0
var lastcategoryfilter : Int! = 0

Then I created a couple of functions for saving and loading the values to NSUserDefaults:

然后我创建了几个函数来保存和加载NSUserDefaults的值:

func LoadLastCategoryFilter()
{
    let defaults = NSUserDefaults.standardUserDefaults()
    lastcategoryfilter = defaults.integerForKey("ChosenCategory")
}


func SaveLastCategoryFilter()
{
    lastcategoryfilter = categoryfilter
    let defaults = NSUserDefaults.standardUserDefaults()
    defaults.setInteger(lastcategoryfilter, forKey: "ChosenCategory")
}

Then I use buttons so the user can set a value for the variables and switch to the 'game' screen:

然后我使用按钮,以便用户可以设置变量的值并切换到“游戏”屏幕:

@IBAction func buttonAstronomyCategory(sender: AnyObject) {

    categoryfilter = 1
    SaveLastCategoryFilter()
    ResetGameFromMainMenu()
    switchToMainGameScreen()
}



@IBAction fund buttonSportCategory(sender: AnyObject) {

    categoryfilter = 2
    SaveLastCategoryFilter()
    ResetGameFromMainMenu()
    switchToMainGameScreen()
}

And so on for each of the categories.

等等每个类别。

In the game screen I have a function that loads the lastcategoryfilter value and then I load the .json data as follows:

在游戏屏幕中,我有一个函数加载lastcategoryfilter值,然后我加载.json数据,如下所示:

func LoadAllQuestionsAndAnswers()
{
    let path = NSBundle.mainBundle().pathForResource("content", ofType: "json")
    let jsonData : NSData = NSData(contentsOfFile: path!)!
    allEntries = (try! NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers)) as! NSArray


    if (lastcategoryfilter == 1)
    {
        let filteredEntries = allEntries.filter() {$0.category == "astronomy"}
        print(filteredEntries)

    }else{

        if (lastcategoryfilter == 2)
        {
            let filteredEntries = allEntries.filter() {$0.category == "sport"}
            print(filteredEntries)

        }else{
            if (lastcategoryfilter == 3)
            {
                let filteredEntries = allEntries.filter() {$0.category == "History"}
                print(filteredEntries)

            }else{

                if (lastcategoryfilter == 4)
                {
                    let filteredEntries = allEntries.filter() {$0.category == "Politics"}
                    print(filteredEntries)

                }else{

                    if (lastcategoryfilter == 5)
                    {
                        let filteredEntries = allEntries.filter() {$0.category == "Technology"}
                        print(filteredEntries)

                    }}}}}}

And then I use the same functions as previously described to shuffle/randomise the questions and present them to the user.

然后我使用与前面描述的相同的功能来混淆/随机化问题并将它们呈现给用户。

The code all works fine (i.e. no errors) but my data isn't being filtered.

代码一切正常(即没有错误)但我的数据没有被过滤。

I have no idea where I've gone wrong, but I'm sure it's just a silly newbie mistake! Any suggestions?

我不知道我哪里出错了,但我确定这只是一个愚蠢的新手错误!有什么建议么?

2 个解决方案

#1


1  

filtering your array is easy. Here's a fairly contrived example which doesn't address the JSON or random questions, but does show the filtering part. Obviously, in your example, you're not going to create named arrays like I have here, but the example works nicely.

过滤你的阵列很容易。这是一个相当人为的例子,它没有解决JSON或随机问题,但确实显示了过滤部分。显然,在你的例子中,你不会像我这里那样创建命名数组,但是这个例子运行得很好。

struct QuestionStruct
{
    var id : Int
    var question : String
    var category : String
    var answer : String
}

class ViewController: UIViewController
{
    override func viewDidLoad()
    {
        super.viewDidLoad()

        var questions : [QuestionStruct] = []

        questions.append(QuestionStruct(id: 1, question:"question 1", category:"English", answer:"answer 1"))
        questions.append(QuestionStruct(id: 2, question:"question 2", category:"Physics", answer:"answer 2"))
        questions.append(QuestionStruct(id: 3, question:"question 3", category:"Physics", answer:"answer 3"))
        questions.append(QuestionStruct(id: 4, question:"question 4", category:"Maths", answer:"answer 4"))
        questions.append(QuestionStruct(id: 5, question:"question 5", category:"English", answer:"answer 5"))

        let filteredQuestionsPhysics = questions.filter() { $0.category == "Physics" }
        let filteredQuestionsEnglish = questions.filter() { $0.category == "English" }
        let filteredQuestionsMaths = questions.filter() { $0.category == "Maths" }
    }
}

more specifically, in your example, you would probably want to amend LoadAllQuestionsAndAnswers to return a specific list of questions rather than everything. Thereafter, you can randomise it exactly as before

更具体地说,在您的示例中,您可能希望修改LoadAllQuestionsAndAnswers以返回特定的问题列表而不是所有内容。此后,您可以像以前一样随机化它

func LoadAllQuestionsAndAnswers()
{
    let path = NSBundle.mainBundle().pathForResource("content", ofType: "json")
    let jsonData : NSData = NSData(contentsOfFile: path!)!
    allEntries = (try! NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers)) as! NSArray
    //println(allEntries)
    let filteredEntries = allEntries.filter() {$0.category == categoryFilter}
    println(filteredEntries)
}

#2


1  

OK Monomeeth - there are some things we can do to improve the readability and maintainability of your code. For a start - we can remove all of those nested if statements and make things a little more generic everywhere.

OK Monomeeth - 我们可以采取一些措施来提高代码的可读性和可维护性。首先,我们可以删除所有嵌套的if语句,并使所有内容更加通用。

Although you will have a series of buttons - you can have them all accessing the same method, because you're doing the same thing each time. if you set the tag value of each button to an index value, we will know which button brought us to the method. If you then store your categories in an array, then we don't need to hard-code Astronomy, we can just use categories[0]

虽然你将有一系列按钮 - 你可以让它们都访问相同的方法,因为你每次都做同样的事情。如果您将每个按钮的标记值设置为索引值,我们将知道哪个按钮将我们带到该方法。如果你然后将类别存储在一个数组中,那么我们不需要硬编码天文学,我们可以只使用类别[0]

Here are the key bits of code you will need

以下是您需要的关键代码

first get all of your definitions in place

首先让你的所有定义到位

class ViewController: UIViewController
{
    var categoryFilter : Int = 0
    var lastcategoryfilter : Int = 0

    let categories : [String] = ["Astronomy", "Sport", "History", "Politics", "Technology"] // if you have LOTS of these, you might want to load in a file and parse it ...
    var questions : [QuestionStruct] = [] // QuestionStruct is whatever structure you need to store the questions
    var filteredEntries : [QuestionStruct] = []

then you need a single method to handle the button press, which ALL of your category selection buttons are attached to

然后你需要一个方法来处理按钮按下,所有类别选择按钮都附加到按钮上

@IBAction func cmdSelectCategory(sender: AnyObject)
{
    // ALL of the buttons for select category are linked to this one method
    // the buttons have a tag value set to act as index

    categoryFilter = sender.tag
    generateFilteredQuestionSet()

}

and then you need to filter the questions, based on the categoryFilter

然后你需要根据categoryFilter过滤问题

func generateFilteredQuestionSet()
{
    filteredEntries = questions.filter() { $0.category == categories[categoryFilter]}
    print(filteredEntries)
}

Good luck!

祝你好运!

#1


1  

filtering your array is easy. Here's a fairly contrived example which doesn't address the JSON or random questions, but does show the filtering part. Obviously, in your example, you're not going to create named arrays like I have here, but the example works nicely.

过滤你的阵列很容易。这是一个相当人为的例子,它没有解决JSON或随机问题,但确实显示了过滤部分。显然,在你的例子中,你不会像我这里那样创建命名数组,但是这个例子运行得很好。

struct QuestionStruct
{
    var id : Int
    var question : String
    var category : String
    var answer : String
}

class ViewController: UIViewController
{
    override func viewDidLoad()
    {
        super.viewDidLoad()

        var questions : [QuestionStruct] = []

        questions.append(QuestionStruct(id: 1, question:"question 1", category:"English", answer:"answer 1"))
        questions.append(QuestionStruct(id: 2, question:"question 2", category:"Physics", answer:"answer 2"))
        questions.append(QuestionStruct(id: 3, question:"question 3", category:"Physics", answer:"answer 3"))
        questions.append(QuestionStruct(id: 4, question:"question 4", category:"Maths", answer:"answer 4"))
        questions.append(QuestionStruct(id: 5, question:"question 5", category:"English", answer:"answer 5"))

        let filteredQuestionsPhysics = questions.filter() { $0.category == "Physics" }
        let filteredQuestionsEnglish = questions.filter() { $0.category == "English" }
        let filteredQuestionsMaths = questions.filter() { $0.category == "Maths" }
    }
}

more specifically, in your example, you would probably want to amend LoadAllQuestionsAndAnswers to return a specific list of questions rather than everything. Thereafter, you can randomise it exactly as before

更具体地说,在您的示例中,您可能希望修改LoadAllQuestionsAndAnswers以返回特定的问题列表而不是所有内容。此后,您可以像以前一样随机化它

func LoadAllQuestionsAndAnswers()
{
    let path = NSBundle.mainBundle().pathForResource("content", ofType: "json")
    let jsonData : NSData = NSData(contentsOfFile: path!)!
    allEntries = (try! NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers)) as! NSArray
    //println(allEntries)
    let filteredEntries = allEntries.filter() {$0.category == categoryFilter}
    println(filteredEntries)
}

#2


1  

OK Monomeeth - there are some things we can do to improve the readability and maintainability of your code. For a start - we can remove all of those nested if statements and make things a little more generic everywhere.

OK Monomeeth - 我们可以采取一些措施来提高代码的可读性和可维护性。首先,我们可以删除所有嵌套的if语句,并使所有内容更加通用。

Although you will have a series of buttons - you can have them all accessing the same method, because you're doing the same thing each time. if you set the tag value of each button to an index value, we will know which button brought us to the method. If you then store your categories in an array, then we don't need to hard-code Astronomy, we can just use categories[0]

虽然你将有一系列按钮 - 你可以让它们都访问相同的方法,因为你每次都做同样的事情。如果您将每个按钮的标记值设置为索引值,我们将知道哪个按钮将我们带到该方法。如果你然后将类别存储在一个数组中,那么我们不需要硬编码天文学,我们可以只使用类别[0]

Here are the key bits of code you will need

以下是您需要的关键代码

first get all of your definitions in place

首先让你的所有定义到位

class ViewController: UIViewController
{
    var categoryFilter : Int = 0
    var lastcategoryfilter : Int = 0

    let categories : [String] = ["Astronomy", "Sport", "History", "Politics", "Technology"] // if you have LOTS of these, you might want to load in a file and parse it ...
    var questions : [QuestionStruct] = [] // QuestionStruct is whatever structure you need to store the questions
    var filteredEntries : [QuestionStruct] = []

then you need a single method to handle the button press, which ALL of your category selection buttons are attached to

然后你需要一个方法来处理按钮按下,所有类别选择按钮都附加到按钮上

@IBAction func cmdSelectCategory(sender: AnyObject)
{
    // ALL of the buttons for select category are linked to this one method
    // the buttons have a tag value set to act as index

    categoryFilter = sender.tag
    generateFilteredQuestionSet()

}

and then you need to filter the questions, based on the categoryFilter

然后你需要根据categoryFilter过滤问题

func generateFilteredQuestionSet()
{
    filteredEntries = questions.filter() { $0.category == categories[categoryFilter]}
    print(filteredEntries)
}

Good luck!

祝你好运!