Skip to content

Instantly share code, notes, and snippets.

@AvdLee
Created August 7, 2019 15:24
Show Gist options
  • Save AvdLee/af8fc9406b21ea4f343c3546a9cb759d to your computer and use it in GitHub Desktop.
Save AvdLee/af8fc9406b21ea4f343c3546a9cb759d to your computer and use it in GitHub Desktop.
A Flattener written in Swift
/// - We have multiple files with a X amount of chunks
/// - Each batch request can contain a max of 50 chunks from possibly multiple files
///
/// Challenge: Distribute the chunk requests over as less batch requests as possible
import UIKit
import Foundation
typealias PublicIdentifier = String
struct BatchContentUploadInfoRequest: Encodable {
private static let maxPartCount = 50
let items: [ContentUploadInfoRequest]
/// The total number of parts requested in this batch request.
var totalPartsCount: Int {
return items.reduce(0, { (totalCount, request) -> Int in
return totalCount + request.totalPartsCount
})
}
init(items: inout [ContentUploadInfoRequest]) {
self.items = items.take(max: BatchContentUploadInfoRequest.maxPartCount)
}
func offset(for publicIdentifier: PublicIdentifier) -> Int? {
return items.first(where: { $0.fileIdentifier == publicIdentifier })?.offset
}
}
struct ContentUploadInfoRequest: Encodable {
private enum CodingKeys: String, CodingKey {
case offset, limit
case fileIdentifier = "file_id"
}
let fileIdentifier: PublicIdentifier
private(set) var offset: Int
private(set) var limit: Int
var totalPartsCount: Int {
return limit
}
mutating func take(max: Int) -> ContentUploadInfoRequest {
let takingPartsCount = min(limit, max)
let request = ContentUploadInfoRequest(fileIdentifier: fileIdentifier, offset: offset, limit: takingPartsCount)
offset += takingPartsCount
limit -= takingPartsCount
return request
}
}
extension Array where Element == ContentUploadInfoRequest {
func mapToBatchRequests() -> [BatchContentUploadInfoRequest] {
var mutableSelf = self
var batchRequests: [BatchContentUploadInfoRequest] = []
while !mutableSelf.isEmpty {
batchRequests.append(BatchContentUploadInfoRequest(items: &mutableSelf))
}
return batchRequests
}
mutating func take(max: Int) -> [Element] {
var takenPartsCount = 0
var takenRequests: [Element] = []
var newSelf = self
defer {
self = newSelf
}
while takenPartsCount != max, !newSelf.isEmpty {
let remainingPartsCount = max - takenPartsCount
var request: Element = newSelf.removeFirst()
takenRequests.append(request.take(max: remainingPartsCount))
takenPartsCount += takenRequests.last!.totalPartsCount
if request.totalPartsCount != 0 {
newSelf.insert(request, at: 0)
}
}
return takenRequests
}
}
let contentUploadInfoRequests = (0..<10).map { index -> ContentUploadInfoRequest in
let offset = (0..<10).randomElement()!
let limit = (10..<110).randomElement()!
return ContentUploadInfoRequest(fileIdentifier: "File \(index + 1)", offset: offset, limit: limit)
}
contentUploadInfoRequests.forEach {
print("Total parts: \($0.totalPartsCount) Offset: \($0.offset) limit: \($0.limit)")
}
contentUploadInfoRequests
.mapToBatchRequests()
.forEach { batchRequest in
let fileNames = batchRequest.items.map { ($0.fileIdentifier, "Parts: \($0.totalPartsCount)") }
print("Batch request contains \(batchRequest.totalPartsCount) parts. Filenames: \(fileNames)")
}
/* Example output running this code in a playground:
Total parts: 13 Offset: 2 limit: 13
Total parts: 104 Offset: 2 limit: 104
Total parts: 41 Offset: 9 limit: 41
Total parts: 96 Offset: 2 limit: 96
Total parts: 60 Offset: 0 limit: 60
Total parts: 16 Offset: 5 limit: 16
Total parts: 75 Offset: 4 limit: 75
Total parts: 85 Offset: 3 limit: 85
Total parts: 45 Offset: 0 limit: 45
Total parts: 88 Offset: 0 limit: 88
Batch request contains 50 parts. Filenames: [("File 1", "Parts: 13"), ("File 2", "Parts: 37")]
Batch request contains 50 parts. Filenames: [("File 2", "Parts: 50")]
Batch request contains 50 parts. Filenames: [("File 2", "Parts: 17"), ("File 3", "Parts: 33")]
Batch request contains 50 parts. Filenames: [("File 3", "Parts: 8"), ("File 4", "Parts: 42")]
Batch request contains 50 parts. Filenames: [("File 4", "Parts: 50")]
Batch request contains 50 parts. Filenames: [("File 4", "Parts: 4"), ("File 5", "Parts: 46")]
Batch request contains 50 parts. Filenames: [("File 5", "Parts: 14"), ("File 6", "Parts: 16"), ("File 7", "Parts: 20")]
Batch request contains 50 parts. Filenames: [("File 7", "Parts: 50")]
Batch request contains 50 parts. Filenames: [("File 7", "Parts: 5"), ("File 8", "Parts: 45")]
Batch request contains 50 parts. Filenames: [("File 8", "Parts: 40"), ("File 9", "Parts: 10")]
Batch request contains 50 parts. Filenames: [("File 9", "Parts: 35"), ("File 10", "Parts: 15")]
Batch request contains 50 parts. Filenames: [("File 10", "Parts: 50")]
Batch request contains 23 parts. Filenames: [("File 10", "Parts: 23")]
*/
@AvdLee
Copy link
Author

AvdLee commented Aug 7, 2019

Who's up for a challenge? I'm trying to make this code as efficient as possible!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment