I'm not sure if this is possible! But maybe the Stackoverflow community can help.
Description of work:
I have created an array of structs [imageFileNames]
to help me sort, filter and map a bunch of jpg files. Using the same array of structs, I want to populate a table view that's nested in a collection view. The collection view will use the key inputs as labels and the table view will use the values to populate the table view cells.
Problem: In order to populate both the Collection Cell and Table View, I'd like to convert into an array of dictionaries but instead of having the dictionary values as structs, I'd like to convert them into strings. I use the part property in my struct to populate the keys in the dictionary. Below I displayed my struct build, array of structs and my ideal goal.
Can you all help me build a function that can manage the conversion?
Struct:
struct imageFileNames {
var scene: String
var object: String
var objectNumber: String
var part: String
var frame: String
init(scene:String, object:String, objectNumber: String, part:String, frame:String){
self.scene = scene
self.object = object
self.objectNumber = objectNumber
self.part = part
self.frame = frame
}
}
Current:
var notFinal: [imageFileNames] =
CloneAnimation_20200907.imageFileNames(scene: "scene01", object: "character", objectNumber: "1", part: "kneeRight", frame: "00.png"),
CloneAnimation_20200907.imageFileNames(scene: "scene01", object: "character", objectNumber: "1", part: "lefteyelid", frame: "01.png"),
CloneAnimation_20200907.imageFileNames(scene: "scene01", object: "character", objectNumber: "1", part: "lefteyelid", frame: "02.png"),
CloneAnimation_20200907.imageFileNames(scene: "scene01", object: "character", objectNumber: "1", part: "lefteyelid", frame: "03.png"),
CloneAnimation_20200907.imageFileNames(scene: "scene01", object: "character", objectNumber: "1", part: "lefteyelid", frame: "04.png"),
CloneAnimation_20200907.imageFileNames(scene: "scene01", object: "character", objectNumber: "1", part: "lefteyelid", frame: "05.png"),
CloneAnimation_20200907.imageFileNames(scene: "scene01", object: "character", objectNumber: "1", part: "mouthAngry", frame: "00.png"),
CloneAnimation_20200907.imageFileNames(scene: "scene01", object: "character", objectNumber: "1", part: "mouthAngry", frame: "01.png"),
CloneAnimation_20200907.imageFileNames(scene: "scene01", object: "character", objectNumber: "1", part: "mouthAngry", frame: "02.png"),
CloneAnimation_20200907.imageFileNames(scene: "scene01", object: "character", objectNumber: "1", part: "mouthAngry", frame: "03.png"),
]
Goal:
var goalFinal: [String: [String]] = [
"lefteyelid": [
"scene01_character_1_lefteyelid_01",
"scene01_character_1_lefteyelid_02",
"scene01_character_1_lefteyelid_03",
"scene01_character_1_lefteyelid_04"
],
"mouthAngry": [
"scene01_character_1_mouthAngry_00",
"scene01_character_1_mouthAngry_01",
"scene01_character_1_mouthAngry_02",
"scene01_character_1_mouthAngry_03",
]
]
My Attempts: As you can see from my attempts, I'm pretty stuck on the matter.
Parsing out key value:
- I tried to use the
let dict = notFinal.toDictionary {$0.part}
method to parse out the key input and place corresponding structs into an array as values, however this method doesn't work because it only shows one of the values with the same part name.
Convert struct into string:
- I was able to create a function that takes the structure and combines all the properties into one string but is worthless without the initial part.
func convertStructIntoString (structHold: [imageFileNames])->[String] {
var temp: [imageFileNames] = structHold
let final = temp.map {$0.scene "_" $0.object "_" $0.objectNumber "_" $0.part "_" $0.frame}
return final
}
CodePudding user response:
Before providing the answer, let me rename your class to ImageFileName
to make the code more legible, so we get this (you may think about coming up with some other name as the struct doesn't really seem to be a file name):
struct ImageFileName {
var scene: String
var object: String
var objectNumber: String
var part: String
var frame: String
}
It's probably a good idea to make your type names uppercase and the constructor you provided is not really needed for structs - it gets generated automatically.
Now the function that performs the conversion you're looking for would be:
func finalize(_ imageFileNames: [ImageFileName]) -> [String: [String]] {
Dictionary(grouping: imageFileNames) { imageFileName in
imageFileName.part
}
.mapValues { groupImageFileNames in
groupImageFileNames.map { imageFileName in
"\(imageFileName.scene)_\(imageFileName.object)_\(imageFileName.part)_\(URL(string: imageFileName.frame)!.deletingPathExtension())"
}
}
}
or, the more concise version doing the same thing:
func finalize(_ imageFileNames: [ImageFileName]) -> [String: [String]] {
Dictionary(grouping: imageFileNames, by: { $0.part })
.mapValues { $0.map { "\($0.scene)_\($0.object)_\($0.part)_\(URL(string: $0.frame)!.deletingPathExtension())" } }
}
so you can do:
let notFinal: [ImageFileName] = [
ImageFileName(scene: "scene01", object: "character", objectNumber: "1", part: "kneeRight", frame: "00.png"),
ImageFileName(scene: "scene01", object: "character", objectNumber: "1", part: "lefteyelid", frame: "01.png"),
ImageFileName(scene: "scene01", object: "character", objectNumber: "1", part: "lefteyelid", frame: "02.png"),
ImageFileName(scene: "scene01", object: "character", objectNumber: "1", part: "lefteyelid", frame: "03.png"),
ImageFileName(scene: "scene01", object: "character", objectNumber: "1", part: "lefteyelid", frame: "04.png"),
ImageFileName(scene: "scene01", object: "character", objectNumber: "1", part: "lefteyelid", frame: "05.png"),
ImageFileName(scene: "scene01", object: "character", objectNumber: "1", part: "mouthAngry", frame: "00.png"),
ImageFileName(scene: "scene01", object: "character", objectNumber: "1", part: "mouthAngry", frame: "01.png"),
ImageFileName(scene: "scene01", object: "character", objectNumber: "1", part: "mouthAngry", frame: "02.png"),
ImageFileName(scene: "scene01", object: "character", objectNumber: "1", part: "mouthAngry", frame: "03.png")
]
let goalFinal = finalize(notFinal)
EDITED: modified finalized
function to add trailing frame index, as pointed out by @Jessy
CodePudding user response:
Adopt
CustomStringConvertible
.import Foundation extension imageFileNames: CustomStringConvertible { var description: String { """ \(scene)_\(object)_\(objectNumber)_\(part)_\ \(URL(string: frame)!.deletingPathExtension()) """ } }
Do what the other people are telling you:
import struct Collections.OrderedDictionary extension OrderedDictionary where Key == String, Value == [String] { init(_ imageFileNamesSequence: some Sequence<imageFileNames>) { self = OrderedDictionary<_, _>(grouping: imageFileNamesSequence, by: \.part) .mapValues { $0.map(String.init) } } }