Created
August 7, 2019 15:24
-
-
Save AvdLee/af8fc9406b21ea4f343c3546a9cb759d to your computer and use it in GitHub Desktop.
A Flattener written in Swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// - 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")] | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Who's up for a challenge? I'm trying to make this code as efficient as possible!