Как изменить ключи объекта json на основе другого клиента

#javascript #api #object #recursion

#javascript #API #объект #рекурсия

Вопрос:

у меня есть требование, при котором ключ ответа API будет меняться в зависимости от клиента, например, если у меня есть два клиента, клиент 1 и клиент 2, и мои исходные данные json, как показано ниже

 {
  "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"
}
 

Но для клиента 1 ключ должен быть другим, например, destination_addresses будет dest_address, а для клиента 2 ключом будет destaddress, и это изменение ключа может быть выполнено до дочернего уровня, например, ключ расстояния будет dist для клиента 1, а для клиента 2 это будет что-то другое, поэтому для клиента 1
Например, приведенный выше ответ для клиента 1 будет выглядеть следующим образом

 {
  "dest_address": [
    "Washington, DC, USA",
    "Philadelphia, PA, USA",
    "Santa Barbara, CA, USA",
    "Miami, FL, USA",
    "Austin, TX, USA",
    "Napa County, CA, USA"
  ],
  "og_addresses": [
    "New York, NY, USA"
  ],
  "row1": [{
    "elements_client": [{
        "dist": {
          "t": "227 mi",
          "v": 365468
        },
        "duration": {
          "t": "3 hours 54 mins",
          "value": 14064
        },
        "status": "OK"
      },
      {
        "dist": {
          "t": "94.6 mi",
          "value": 152193
        },
        "duration": {
          "t": "1 hour 44 mins",
          "value": 6227
        },
        "status": "OK"
      },
      {
        "dist": {
          "t": "2,878 mi",
          "value": 4632197
        },
        "duration": {
          "t": "1 day 18 hours",
          "value": 151772
        },
        "status": "OK"
      },
      {
        "dist": {
          "t": "1,286 mi",
          "value": 2069031
        },
        "duration": {
          "t": "18 hours 43 mins",
          "value": 67405
        },
        "status": "OK"
      },
      {
        "dist": {
          "t": "1,742 mi",
          "value": 2802972
        },
        "duration": {
          "t": "1 day 2 hours",
          "value": 93070
        },
        "status": "OK"
      },
      {
        "dist": {
          "t": "2,871 mi",
          "value": 4620514
        },
        "duration": {
          "t": "1 day 18 hours",
          "value": 152913
        },
        "status": "OK"
      }
    ]
  }],
  "status": "OK"
}
 

и это могут быть разные ключи для клиента 2, так как я могу добиться этого, поскольку объект может быть вложенным, а клиентов может быть много, я перепробовал много вещей, таких как рекурсия, и все, но не смог этого сделать, и его специфика для клиента, наконец, опубликовал мой запрос здесь, нижеявляется ли код, который я пишу, поскольку вы можете видеть родительский ключ, я могу изменить его, но вложенный я не знаю, как я могу это сделать

 const keys = {
             "destination_addresses":"dest_address",
             "origin_addresses" : "org_address"   
}
const newObj = {};

recursivelyIterateProperties(obj)
function recursivelyIterateProperties(jsonObject,key) {
    if(key){
        if(keys[key]){
            newObj[keys[key]] = jsonObject
        }
    }
    console.log(jsonObject,'<----->>>>>',key)
    if (jsonObject instanceof Array) {
        const d = jsonObject[0];
        if (typeof(d) === 'object'){
            const obj = Object.keys(jsonObject)
            for (var prop in obj) {
                if (!(typeof(jsonObject[obj[prop]]) === 'string')) {
                    recursivelyIterateProperties(jsonObject[obj[prop]],obj[prop]);
                }
            }
        }
        // for (var i = 0; i < jsonObject.length;   i) {
        //     recursivelyIterateProperties(jsonObject[i],key)
        // }
    }
    else if (typeof(jsonObject) === 'object') {
        const obj = Object.keys(jsonObject)
        for (var prop in obj) {
            if (!(typeof(jsonObject[obj[prop]]) === 'string')) {
                recursivelyIterateProperties(jsonObject[obj[prop]],obj[prop]);
            }else{

            }
        }
    }
}

console.log(newObj);
 

Комментарии:

1. Вам потребуется сопоставление между исходными ключами и ключами целевого клиента. Затем примените это сопоставление к объекту.

2. Я попытался создать объект сопоставления, но не смог создать его должным образом, учитывая сценарий вложенного объекта

3. Не могли бы вы показать, что вы пробовали до сих пор. Возможно, вы довольно близки.

Ответ №1:

Вот потенциальный ответ. Я не в восторге от этого, но у меня больше нет времени работать над этим сейчас.

Идея состоит в том, чтобы использовать объект конфигурации, подобный этому:

 {
  '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',
}
 

и код, который сглаживает ваш входной объект в пары ключ-значение, где ключи записывают структуру объекта, что-то вроде этого:

 [
  ["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"]
]
 

И преобразование этого в

 [
  ["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"]
]
 

А затем, наконец, повторно преобразовать его обратно в реальный объект.

Код в следующем фрагменте делает это. Он использует некоторые функции с API, заимствованные из Ramda (отказ от ответственности: Я один из авторов), хотя реализации здесь разные. И он использует ряд общих служебных функций, таких как getPaths , pathEntries , hydrate , и reconstruct , а затем несколько вспомогательных функций, все из которых служат для того, чтобы сделать основную функцию, convertStructure , довольно простой.

 // Utility functions borrowed from Ramda
const path = (ps = []) => (obj = {}) =>
  ps .reduce ((o, p) => (o || {}) [p], obj)

const assoc = (prop, val, obj) => 
  Number .isInteger (prop) amp;amp; Array .isArray (obj)
    ? [... obj .slice (0, prop), val, ...obj .slice (prop   1)]
    : {...obj, [prop]: val}

const assocPath = ([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 functions
const getPaths = (obj) =>
  Object (obj) === obj
    ? Object .entries (obj) .flatMap (
        ([k, v]) => getPaths (v) .map (p => k   (p ? '|' : '')   p)
      )
    : ['']

const pathEntries = (obj) => 
  getPaths (obj) .map (p => [p, path (p.split('|')) (obj)])

const fixPath = (p) =>
  p .split ('|') .map (n => /^d $/.test(n) ? Number(n) : n)

const hydrate = (entries) =>
  entries .reduce ((a, [k, v]) => assocPath (fixPath (k), v, a), {})

const reconstruct = (fn) => (obj) => 
  hydrate (fn (pathEntries (obj)))


// Helper functions
const addHash = (config) => (key) =>
  key .split ('|') .map (k => /^d $/ .test (k) ? '#' : k) .join ('|')

const convertKey = (config) => (key, hashed = addHash (config) (key)) =>
  hashed in config ? config [hashed] : hashed

const reconstituteKey = (template, key, parts = key .split ('|')) => 
  template .split ('|') .map ((k, i) => k == '#' ? parts [i] : k) .join ('|')


// Main function
const convertStructure = (config) => 
  reconstruct (pathEntries => pathEntries .map (
    ([k, v]) => [reconstituteKey (convertKey (config) (k), k), v])
  )


// Sample input
const 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',
}

// Demo
console .log (convertStructure (config) (input)) 
 .as-console-wrapper {max-height: 100% !important; top: 0} 

У этого решения есть несколько недостатков. Во-первых, используя строковые ключи, мы ограничиваем имена свойств элементов, с которыми мы можем работать. Обычно я бы предпочел работать с ['rows', 0, 'elements', 1, 'distance', 'text'] вместо 'rows|0|elements|1|distance|text' . Есть много веских причин, но этот формат делает конфигурацию гораздо менее удобной. Обратите внимание, однако, что если у вас есть имена свойств, которые включают '|' или '#' , часть этого кода придется изменить.

Во-вторых, это решение требует от вас переименования конечных путей. Если бы мы хотели переименовать 'rows|#|elements|#|duration' в 'rows|#|elements|#|time' , нам пришлось бы обрабатывать как text свойство, так и value свойство, даже если мы их не меняем. Я бы хотел увидеть вариант, который не требует этого, но у меня нет хорошей альтернативы.

В любом случае, я надеюсь, что это работает для ваших нужд. И более того, я надеюсь, что другие могут предложить лучшие решения.