Skip to content

Commit

Permalink
Performance fix: created two valueFor functions (one for dictionaries…
Browse files Browse the repository at this point in the history
… and one for arrays) to remove the need to cast the JSON dictionary input

Casting the dictionary was very slow especially given a large JSON input
  • Loading branch information
tristanhimmelman committed Nov 3, 2015
1 parent 33a2e63 commit 3656dc4
Showing 1 changed file with 29 additions and 23 deletions.
52 changes: 29 additions & 23 deletions ObjectMapper/Core/Map.swift
Expand Up @@ -63,7 +63,7 @@ public final class Map {
currentValue = JSONDictionary[key]
} else {
// break down the components of the key that are separated by .
currentValue = valueFor(ArraySlice(key.componentsSeparatedByString(".")), collection: JSONDictionary)
currentValue = valueFor(ArraySlice(key.componentsSeparatedByString(".")), dictionary: JSONDictionary)
}

return self
Expand Down Expand Up @@ -101,44 +101,50 @@ public final class Map {
}
}

/// Fetch value from JSON dictionary, loop through them until we reach the desired object.
private func valueFor(keyPathComponents: ArraySlice<String>, collection: AnyObject?) -> AnyObject? {
/// Fetch value from JSON dictionary, loop through keyPathComponents until we reach the desired object
private func valueFor(keyPathComponents: ArraySlice<String>, dictionary: [String: AnyObject]) -> AnyObject? {
// Implement it as a tail recursive function.

if keyPathComponents.isEmpty {
return nil
}

//optional object to keep optional retreived from collection
var optionalObject: AnyObject?
if let keyPath = keyPathComponents.first {
let object = dictionary[keyPath]
if object is NSNull {
return nil
} else if let dict = object as? [String : AnyObject] where keyPathComponents.count > 1 {
let tail = keyPathComponents.dropFirst()
return valueFor(tail, dictionary: dict)
} else {
return object
}
}

//check if collection is dictionary or array (if it's array, also try to convert keypath to Int as index)
if let dictionary = collection as? [String : AnyObject],
let keyPath = keyPathComponents.first {
return nil
}

//keep retreved optional
optionalObject = dictionary[keyPath]
} else if let array = collection as? [AnyObject],
let keyPath = keyPathComponents.first,
index = Int(keyPath) {

//keep retreved optional
optionalObject = array[index]
/// Fetch value from JSON Array, loop through keyPathComponents them until we reach the desired object
private func valueFor(keyPathComponents: ArraySlice<String>, dictionary: [AnyObject]) -> AnyObject? {
// Implement it as a tail recursive function.

if keyPathComponents.isEmpty {
return nil
}

if let object = optionalObject {
//Try to convert keypath to Int as index)
if let keyPath = keyPathComponents.first,
let index = Int(keyPath) {

let object = dictionary[index]

if object is NSNull {
return nil
} else if let dict = object as? [String : AnyObject] where keyPathComponents.count > 1 {
let tail = keyPathComponents.dropFirst()
return valueFor(tail, collection: dict)
} else if let dict = object as? [AnyObject] where keyPathComponents.count > 1 {
let tail = keyPathComponents.dropFirst()
return valueFor(tail, collection: dict)
return valueFor(tail, dictionary: dict)
} else {
return object
}
}

return nil
}

2 comments on commit 3656dc4

@kaandedeoglu
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tristanhimmelman this really made a difference, according to my benchmarks - thank you!!

@tristanhimmelman
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! Glad to improve things 😄

Please sign in to comment.