Complex Dictionary Get Values as Strings With Conditions

55 views Asked by At

I have built a survey using laanlabs/SwiftSurvey and I am able to get the results from the surveyComplete method as a dictionary.

The results return in a complex structure and I need to get the values for each response by the key tag of the questions array -> question object. This object contains an array of choices and within each choice object there is a key of selected. If the selected key's value is true (1) I need to get the text key's value that is in the same object. Some of these choices will have multiple selected keys with a value of true (1), if this is the case I'd like to concatenate the text key values with a comma in between the values.

The intention is then to insert the keys in to a SQLite database.

I am new to decoding dictionaries and traversing them in the correct way, I can access the dictionary print(dictionary) and also get into the the correct NSArray - print(dictionary["questions"] but from there I am stumped, could someone show me how please.

The results are below unfortunately its a large block apologies.

[
    "version": 001, 
    "metadata": 
    {
        "app_version" = "1.1";
        build = 22;
        debug = true;
    }, 
    "questions": <__NSArrayI 0x600000614d20>(
        {
            question =     {
                allowsMultipleSelection = 0;
                choices =         (
                                {
                        allowsCustomTextEntry = 0;
                        selected = 1;
                        text = Physician;
                        uuid = "224E1B76-D220-4068-AA22-6861E5F836CB";
                    },
                                {
                        allowsCustomTextEntry = 0;
                        selected = 0;
                        text = Dietitian;
                        uuid = "2DB2B6FB-E344-4BBF-A551-2FABE0DFF6AA";
                    },
                                {
                        allowsCustomTextEntry = 0;
                        selected = 0;
                        text = "Genetic Counsellor";
                        uuid = "A9BE7093-B95C-4BF4-B629-12FDA3154ABE";
                    },
                                {
                        allowsCustomTextEntry = 0;
                        selected = 0;
                        text = "Nurse/Nurse Practitioner/Physician Assistant";
                        uuid = "8E75A41B-0D8C-4ADA-A31C-2BC408F8269D";
                    },
                                {
                        allowsCustomTextEntry = 0;
                        selected = 0;
                        text = "Pharmacist / Pharmaceutical Industry";
                        uuid = "C943430D-EA48-4BCB-8ADF-011A223BDF36";
                    },
                                {
                        allowsCustomTextEntry = 0;
                        selected = 0;
                        text = "Academic/Researcher";
                        uuid = "E28377A4-37FC-4351-A857-88383A3D5A3B";
                    },
                                {
                        allowsCustomTextEntry = 0;
                        selected = 0;
                        text = "Patient/Patient Advocacy Group";
                        uuid = "E5836187-6C08-4272-A88E-40578F4FCF44";
                    },
                                {
                        allowsCustomTextEntry = 1;
                        selected = 0;
                        text = "Other (please specify)";
                        uuid = "EFF22342-48A9-4B8E-81A0-BB44D0E86EBC";
                    }
                );
                required = 1;
                tag = "hcp-specialty";
                title = "Please select the option that best describes your specialty:";
                uuid = "7F77E248-8429-463E-9291-241B94BEE4F8";
            };
            type = 0;
        },
        {
            question =     {
                autoAdvanceOnChoice = 1;
                choices =         (
                                {
                        allowsCustomTextEntry = 0;
                        selected = 1;
                        text = Yes;
                        uuid = "3C7A330D-F16B-4F3E-8ABC-6767A1A6332A";
                    },
                                {
                        allowsCustomTextEntry = 0;
                        selected = 0;
                        text = No;
                        uuid = "0E4F5360-FCCD-4860-9971-86E23BB8F6C1";
                    }
                );
                required = 1;
                tag = "newborn-screening";
                title = "Is newborn screening for classical homocystinuria available in your region/country?";
                uuid = "F7C1A9D5-43AB-420D-80CF-F6644B95C73E";
            };
            type = 1;
        },
        {
            question =     {
                feedback = "This is a free text response";
                required = 1;
                tag = "biggest-unmet-need";
                title = "What do you believe is the biggest unmet need for patients living with classical HCU?";
                uuid = "133E2EDC-8FF4-48D1-8BFA-3A20E5DA0052";
            };
            type = 4;
        }
    )
]
1

There are 1 answers

0
timbre timbre On

Based on their result example, you are getting JSON. Converting JSON to Dictionary is the easiest, of course, but also is the dirtiest. You are basically getting "whatever", so when it's time to use the data, you have to do a lot of - as you said - "decoding", validation, etc.

Instead of that, create a few Decodable structures that match your response. In this case you just need 3 structures:

struct Survey: Codable {
    let questions: [Question]
}

struct Question: Codable {
    let allowsMultipleSelection: Int?
    let choices: [Choice]?
    let required: Int
    let tag: String
    let title: String
    let uuid: String
    let feedback: String?
}

struct Choice: Codable {
    let allowsCustomTextEntry: Int
    let selected: Int
    let text: String
    let uuid: String
}

(I didn't verify every fields, you can adjust as needed. And you can omit any properties you don't need / don't care about.)

And then you decode it like this:

// Assume jsonData is your original JSON, which you currently decode as dictionary. So instead you do this:

let decoder = JSONDecoder()
let product = try decoder.decode(Survey.self, from: jsonData)

This approach allows you to

  1. Most importantly, having a well-defined data makes working with database easier. You can even implement database encoder, based on your codable structures, which means you don't need to manually walk through columns of the database. Also when. you read from database, you get the same structures, no need to have 2 sets of rules / validations for database and dictionary you decoded.
  2. This also allows you to be confident about data you decoded: it has proper names and types. You don't need to validate it (it was validated for you on decoding). You are in control which properties are required, which defaults to set, and so on. In more complicated cases, you may need to add manual decoding to your structures, but even then this manual decoding is inside the structure itself, easy to change / work with / test. All this instead of giant messy dictionary with "some stuff" in it.