     case keyPath(String)
 }
 
-extension UnboxPath {
-    var components: [String] {
-        switch self {
-        case .key(let key):
-            return [key]
-        case .keyPath(let keyPath):
-            return keyPath.components(separatedBy: ".")
-        }
-    }
-}
-
 extension UnboxPath: CustomStringConvertible {
     var description: String {
         switch self {


private extension Sequence {
    func map<T>(allowInvalidElements: Bool, transform: (Iterator.Element) throws -> T) throws -> [T] {
        return try self.flatMap {
            do {
                return try transform($0)
            } catch {
                if !allowInvalidElements {
                    throw error
                }
                
                return nil
            }
        }
    }
}

private extension Unboxer {
    func unbox<R>(path: UnboxPath, transform: UnboxTransform<R>) throws -> R {
        do {
            switch path {
            case .key(let key):
                let value = try self.dictionary[key].orThrow(UnboxPathError.missingKey(key))
                return try transform(value).orThrow(UnboxPathError.invalidValue(value, key))
            case .keyPath(let keyPath):
                var currentMode = UnboxingMode.dictionary(self.dictionary)
                let components = keyPath.components(separatedBy: ".")
                let lastKey = try components.last.orThrow(UnboxPathError.emptyKeyPath)
                
                try components.forEach { key in
                    switch currentMode {
                    case .dictionary(let dictionary):
                        currentMode = try UnboxingMode(value: dictionary[key].orThrow(UnboxPathError.missingKey(key)))
                    case .array(let array):
                        guard let index = Int(key), index < array.count else {
                            throw UnboxPathError.missingKey(key)
                        }
                        
                        currentMode = UnboxingMode(value: array[index])
                    case .value(let value):
                        throw UnboxPathError.invalidValue(value, key)
                    }
                }
                
                return try transform(currentMode.value).orThrow(UnboxPathError.invalidValue(currentMode.value, lastKey))
            }
        } catch {
            if let publicError = error as? UnboxError {
                throw publicError
