Skip to content

Instantly share code, notes, and snippets.

@antonmedv
Created September 14, 2018 16:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save antonmedv/3aaf9f9648ba213f874b6565d904ccc0 to your computer and use it in GitHub Desktop.
Save antonmedv/3aaf9f9648ba213f874b6565d904ccc0 to your computer and use it in GitHub Desktop.
Function for fast extraction of part of JSON
/**
* Fast extract part of json by path.
* Example: fastJSON('{...}', ['foo', 'bar'])
*
* @param {String} input JSON string
* @param {Array} path Path for extraction
* @returns {String} Extracted JSON string
* @throws SyntaxError
*/
function fastJSON(input, path) {
let lookup = path.shift() // Holds key what we should find next
let record = false // Triggers setting of next variables.
let start = 0, end = 0 // Holds found offsets in input.
const stack = [] // Count brackets, 0 and 1 represents { and [ correspondingly.
let level = 0 // Current depth level.
let on = 1 // What level we are expecting right now.
loop: for (let i = 0, len = input.length; i < len; i++) {
const ch = input[i]
switch (ch) {
case '{':
stack.push(0)
level++
break
case '}':
if (stack.pop() !== 0) {
throw new SyntaxError(`Unexpected token ${ch} in JSON at position ${i}`)
}
level--
if (record && level < on) {
end = i - 1
break loop
}
break
case '[':
stack.push(1)
level++
break
case ']':
if (stack.pop() !== 1) {
throw new SyntaxError(`Unexpected token ${ch} in JSON at position ${i}`)
}
level--
if (record && level < on) {
end = i - 1
break loop
}
break
case ':':
if (record && level === on) {
start = i + 1
}
break
case ',':
if (record && level === on) {
end = i - 1
break loop
}
break
case '"':
let j = ++i // next char after "
// Consume whole string till next " symbol.
for (; j < len; j++) {
const ch = input[j]
if (ch === '"' && input[j - 1] !== '\\') { // Make sure " doesn't escaped.
break
}
}
// Check if current key is what we was looking for.
if (level === on && input.slice(i, j) === lookup) {
if (path.length > 0) {
lookup = path.shift()
on++
} else {
record = true
}
}
i = j // Continue from end of string.
break
}
}
if (start !== 0 && start < end) {
return input.slice(start, end + 1) // We found it.
} else if (level !== 0) {
throw new SyntaxError(`Unexpected end of JSON input`)
}
}
@antonmedv
Copy link
Author

This function really efficient on very big JSON files.

Around 3-4 times faster what jq.

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