How To Change Keys Of Json Object Based On Different Client
Solution 1:
Here is a potential answer. I'm not thrilled with it, but have no more time to work on it now.
The idea is to use a configuration object like this:
{
'destination_addresses|#': 'dest_addresses|#',
'origin_addresses|#': 'og_addresses|#',
'rows|#|elements|#|distance|text': 'rows|#|elements|#|distance|t',
'rows|#|elements|#|distance|value': 'rows|#|elements|#|distance|v',
'rows|#|elements|#|duration|text': 'rows|#|elements|#|duration|t',
'rows|#|elements|#|duration|value': 'rows|#|elements|#|duration|v',
}
and code which flattens your input object into key-value pairs, where the keys record the object structure, something like this:
[
["destination_addresses|0", "Washington, DC, USA"]
["destination_addresses|1", "Philadelphia, PA, USA"]
// ...
["origin_addresses|0", "New York, NY, USA"]
["rows|0|elements|0|distance|text", "227 mi"]
["rows|0|elements|0|distance|value", "365468"]
["rows|0|elements|0|duration|text", "3 hours 54 mins"]
["rows|0|elements|0|duration|value", "14064"]
["rows|0|elements|0|status", "OK"]
["rows|0|elements|1|distance|text", "94.6 mi"]
["rows|0|elements|1|distance|value", "152193"]
// ...
["rows|0|elements|5|duration|text", "1 day 18 hours"]
["rows|0|elements|5|duration|value", "152913"]
["rows|0|elements|5|status", "OK"]
["status", "OK"]
]
And converting this to
[
["dest_addresses|0", "Washington, DC, USA"]
["dest_addresses|1", "Philadelphia, PA, USA"]
// ...
["og_addresses|0", "New York, NY, USA"]
["rows|0|elements|0|distance|t", "227 mi"]
["rows|0|elements|0|distance|v", "365468"]
["rows|0|elements|0|duration|t", "3 hours 54 mins"]
["rows|0|elements|0|duration|v", "14064"]
["rows|0|elements|0|status", "OK"]
["rows|0|elements|1|distance|t", "94.6 mi"]
["rows|0|elements|1|distance|v", "152193"]
// ..
["rows|0|elements|5|duration|t", "1 day 18 hours"]
["rows|0|elements|5|duration|v", "152913"]
["rows|0|elements|5|status", "OK"]
["status", "OK"]
]
And then finally rehydrating that back into an actual object.
The code in the following snippet does this. It uses some functions with APIs borrowed from Ramda (disclaimer: I'm one of the authors), although the implementations here are different. And it uses a number of generic utility functions, such as getPaths
, pathEntries
, hydrate
, and reconstruct
, and then a few helper functions, all of which serve to make the main function, convertStructure
, fairly simple.
// Utility functions borrowed from Ramdaconstpath = (ps = []) => (obj = {}) =>
ps .reduce ((o, p) => (o || {}) [p], obj)
constassoc = (prop, val, obj) =>
Number .isInteger (prop) && Array .isArray (obj)
? [... obj .slice (0, prop), val, ...obj .slice (prop + 1)]
: {...obj, [prop]: val}
constassocPath = ([p = undefined, ...ps], val, obj) =>
p == undefined
? obj
: ps.length == 0
? assoc(p, val, obj)
: assoc(p, assocPath(ps, val, obj[p] || (obj[p] = Number .isInteger (ps[0]) ? [] : {})), obj)
// Generic utility functionsconstgetPaths = (obj) =>
Object (obj) === obj
? Object .entries (obj) .flatMap (
([k, v]) => getPaths (v) .map (p => k + (p ? '|' : '') + p)
)
: ['']
constpathEntries = (obj) =>
getPaths (obj) .map (p => [p, path (p.split('|')) (obj)])
constfixPath = (p) =>
p .split ('|') .map (n =>/^\d+$/.test(n) ? Number(n) : n)
consthydrate = (entries) =>
entries .reduce ((a, [k, v]) => assocPath (fixPath (k), v, a), {})
constreconstruct = (fn) => (obj) =>
hydrate (fn (pathEntries (obj)))
// Helper functionsconstaddHash = (config) => (key) =>
key .split ('|') .map (k =>/^\d+$/ .test (k) ? '#' : k) .join ('|')
constconvertKey = (config) => (key, hashed = addHash (config) (key)) =>
hashed in config ? config [hashed] : hashed
constreconstituteKey = (template, key, parts = key .split ('|')) =>
template .split ('|') .map ((k, i) => k == '#' ? parts [i] : k) .join ('|')
// Main functionconstconvertStructure = (config) =>
reconstruct (pathEntries => pathEntries .map (
([k, v]) => [reconstituteKey (convertKey (config) (k), k), v])
)
// Sample inputconst input = {destination_addresses: ["Washington, DC, USA", "Philadelphia, PA, USA", "Santa Barbara, CA, USA", "Miami, FL, USA", "Austin, TX, USA", "Napa County, CA, USA"], origin_addresses: ["New York, NY, USA"], rows: [{elements: [{distance: {text: "227 mi", value: 365468}, duration: {text: "3 hours 54 mins", value: 14064}, status: "OK"}, {distance: {text: "94.6 mi", value: 152193}, duration: {text: "1 hour 44 mins", value: 6227}, status: "OK"}, {distance: {text: "2,878 mi", value: 4632197}, duration: {text: "1 day 18 hours", value: 151772}, status: "OK"}, {distance: {text: "1,286 mi", value: 2069031}, duration: {text: "18 hours 43 mins", value: 67405}, status: "OK"}, {distance: {text: "1,742 mi", value: 2802972}, duration: {text: "1 day 2 hours", value: 93070}, status: "OK"}, {distance: {text: "2,871 mi", value: 4620514}, duration: {text: "1 day 18 hours", value: 152913}, status: "OK"}]}], status: "OK"}
const config = {
'destination_addresses|#': 'dest_addresses|#',
'origin_addresses|#': 'og_addresses|#',
'rows|#|elements|#|distance|text': 'rows|#|elements|#|distance|t',
'rows|#|elements|#|distance|value': 'rows|#|elements|#|distance|v',
'rows|#|elements|#|duration|text': 'rows|#|elements|#|duration|t',
'rows|#|elements|#|duration|value': 'rows|#|elements|#|duration|v',
}
// Democonsole .log (convertStructure (config) (input))
.as-console-wrapper {max-height: 100%!important; top: 0}
There are several downsides to this solution. First, by using string keys, we restrict the property names of the elements we can work on. Usually I would choose to work with ['rows', 0, 'elements', 1, 'distance', 'text']
instead of 'rows|0|elements|1|distance|text'
. There are many good reasons, but that format makes for a much less convenient configuration. Note, though, that if you have property names that include '|'
or '#'
, some of this code would have to change.
Second, this solution requires you to rename leaf paths. If we wanted to rename 'rows|#|elements|#|duration'
to 'rows|#|elements|#|time'
, we would have to handle both the text
property and the value
property, even if we aren't changing them. I'd love to see a variant which doesn't require this, but I don't have a good alternative in mind.
In any case, I hope it works for your needs. And more, I hope others might offer better solutions.
Post a Comment for "How To Change Keys Of Json Object Based On Different Client"