Home > Net >  Get average of Integer values by comparing 2 different array based on their index
Get average of Integer values by comparing 2 different array based on their index

Time:07-08

I'm stuck in this problem.

Here is my Dictionary of arrays:

{"Image":["22301657205688/Chin2231657205705u3zK.jpeg","22301657205688/Chin2231657205707k6HN.jpeg","22301657205688/Chin2231657205708Ip57.jpeg","22301657205688/Forehead2231657205693CbX9.jpeg","22301657205688/L Cheek2231657205697g8d4.jpeg","22301657205688/Nose22316572057008AGT.jpeg","22301657205688/Nose2231657205702k9OU.jpeg"],"OutputScore":[3,9,9,3,1,3,9],"TotalScore":5.285714285714286}

I need to get the average number of OutputScore for the common Image name like Chin and Nose which are common in an array of Image. How can I filter the common name then compare it with the OutputScore indexes and get the average for the same names?

For Example There is 2 Nose Image name at index 5,6 and I need their average score from OutputScore value 3,9 at the same index.

Please help. Thanks.

CodePudding user response:

Let's start with parsing the JSON, and extract the values:

let jsonStr = """
{
"Image": [
    "22301657205688/Chin2231657205705u3zK.jpeg",
    "22301657205688/Chin2231657205707k6HN.jpeg",
    "22301657205688/Chin2231657205708Ip57.jpeg",
    "22301657205688/Forehead2231657205693CbX9.jpeg",
    "22301657205688/L Cheek2231657205697g8d4.jpeg",
    "22301657205688/Nose22316572057008AGT.jpeg",
    "22301657205688/Nose2231657205702k9OU.jpeg"
],
"OutputScore": [
    3,
    9,
    9,
    3,
    1,
    3,
    9
],
"TotalScore": 5.285714285714286
}
"""

let jsonDict = try! JSONSerialization.jsonObject(with: Data(jsonStr.utf8), options: []) as! [String: Any]
let images = jsonDict["Image"] as! [String]
let scores = jsonDict["OutputScore"] as! [Int]

You need a method to extract the "name" from that partial URL. Here's an attempt to do so. Your full needs aren't clear enough, but it does the trick for your sample.

func extractPart(from: String) -> String? {
    let regex = try! NSRegularExpression(pattern: "\\d \\/([A-Za-z ] )", options: [])
    guard let firstMatch = regex.firstMatch(in: from, options: [], range: NSRange(location: 0, length: from.utf16.count)) else { return nil }
    let partNSRange = firstMatch.range(at: 1)
    guard let partRange = Range(partNSRange, in: from) else { return nil }
    let part = from[partRange]
    return String(part)
}

We need to "link" images[0] & scores[0], images[1] & scores[1], ... images[n] & scores[n]
To do so, we can use zip():

let zip = zip(images, scores)

Now, let's regroup the zip values which have the same part name:

We can use Dictionary(grouping:by:) in order to group the values, transforming it into a Dictionary where keys are the part name, and values the zip couples:

let partDict: [String : [(String, Int)]] = Dictionary(grouping: zip) { anCoupleElement in
    guard let name = extractPart(from: anCoupleElement.0) else {
        print("Couldn't extract part name from \(anCoupleElement.0)")
        return "Unknown Key"
    }
    return name
}
print(partDict)

We can use reduce(into:_:) in order to group the values, transforming it into a Dictionary where keys are the part name, and values the zip couples

let reduced = zip.reduce(into: [String: [(String, Int)]]()) { partialResult, current in
    guard let name = extractPart(from: current.0) else {
        print("Couldn't extract part name from \(current.0)")
        return
    }
    partialResult[name, default: []]  = [current]
}
print(reduced)

Then, we can calculate the average. I choose an iteration, since it's not clear if you have the "Chin", and you need to search the dictionary for it or not. I used a for loop to show them all:

for (aPartName, values) in partDict { //or for (aPartName, values) in reduced
    let average = Float(values.reduce(0) { $0   $1.1 }) / Float(values.count)
    print("For: \(aPartName), average: \(average)")
    print("With values:")
    values.forEach {
        print("\t\($0.0) - \($0.1)")
    }
}

Final Output:

For: Forehead, average: 3.0
With values:
    22301657205688/Forehead2231657205693CbX9.jpeg - 3
For: Nose, average: 6.0
With values:
    22301657205688/Nose22316572057008AGT.jpeg - 3
    22301657205688/Nose2231657205702k9OU.jpeg - 9
For: L Cheek, average: 1.0
With values:
    22301657205688/L Cheek2231657205697g8d4.jpeg - 1
For: Chin, average: 7.0
With values:
    22301657205688/Chin2231657205705u3zK.jpeg - 3
    22301657205688/Chin2231657205707k6HN.jpeg - 9
    22301657205688/Chin2231657205708Ip57.jpeg - 9

CodePudding user response:

If I understood correctly, once you convert that response to an object you would need to process it. You could iterate all elements of the array "Image" and check if the element contains the feature you look for, if they do, add the value in that same index of OutputScore, finally average that.

Assuming Images and OutputScore have the same size, and you have parsed them already, the code would look like:

func getAverageFor(feature: String) -> Double {
    var elementsSum: Int = 0
    var elementsSeen: Int = 0

    for idx in 0 ..< ImageNames.count {
        if ImageNames[idx].contains(feature) {
            elementsSum  = OutputScore[idx]
            elementsSeen  = 1
        }
    }

    if elementsSeen > 0 {
        return Double(elementsSum) / Double(elementsSeen)
    }

    return 0    // I assume that non present features should be 0
}

CodePudding user response:

I'm assuming that you will be converting your response to Model object. So, your model looks something like this

struct ImageParts: Codable {
    let image: [String]
    let outputScore: [Int]
    let totalScore: Double

    enum CodingKeys: String, CodingKey {
        case image = "Image"
        case outputScore = "OutputScore"
        case totalScore = "TotalScore"
    }
}

Once you have your model object you can use below function to get the all averages mapped

func getAvgerageMapped(from model: ImageParts, for parts: [String]) -> [[String: Double]]{
    return parts.map { key -> [String: Double] in
        var op = [Int]()
        model.image.enumerated().reduce(op) { partialResult, sequence in
            if sequence.element.contains(key) {
                op.append(model.outputScore[sequence.offset])
            }
            return op
        }
        var avg = 0.0
        op.reduce(into: avg) { _, i in
            avg  = Double(i)
        }
        avg = avg/Double(op.count)
        return [key:avg]
    }
}

let averages = getAvgerageMapped(from: model, for: ["Chin", "Forehead", "L Cheek", "Nose"])
print(averages) //[["Chin": 7.0], ["Forehead": 3.0], ["L Cheek": 1.0], ["Nose": 6.0]]
  • Related