Сокращение JSON объекта, содержащего JSON объектов, в массив вида [Path, Value] - PullRequest
1 голос
/ 11 февраля 2020

Я пытаюсь уменьшить JSON объект вложенных объектов, массивов и строк в единый массив путей и значений.

Ожидаемый ввод:

appleObjects = {
  'apples': {
    'colors': ['red', 'green', 'yellow'],
    'owner': 'Person1',
    'types': [
      {name: 'fuji', flavor: 'good'}
    ]
  },
  'pears': {
    'colors': ['red', 'green', 'yellow'],
    'owner': 'Person1',
    'types': [
      {name: 'small', flavor: 'surprising'},
      {name: 'large', flavor: 'bold'}
    ]
  }
}

Ожидаемый результат:

appleValues = [
  {path: 'apples.colors', value: 'red'},
  {path: 'apples.colors', value: 'green'},
  {path: 'apples.colors', value: 'yellow'},
  {path: 'apples.owner', value: 'Person1'},
  {path: 'apples.types', value: {name: 'fuji', flavor: 'sweet?'}}
  ...
]

Пока я работаю над использованием вложенных функций Reduce и / или рекурсии, но, как и в веб-приложении, мне интересно, есть ли более эффективный способ или даже библиотека там уже что-то подобное.

Вот то, с чем я работаю до сих пор. В настоящее время «Реакция» жалуется на слишком много рекурсии, поэтому ясно, что это не самый лучший способ для go выполнить это:

  myReducer = (p, obj) => {

    Object.entries(obj ?? []).reduce((acc, currVal, currIdx) => {
      if(typeof currVal === undefined) {
        return acc
      }
      if(typeof currVal === "string") {
        return {
          basePath: acc.basePath,
          outputArr:  acc.outputArr.push({
            path: acc.basePath + '.' + currVal[0],
            value: currVal[1]
          })
        }
      }
      if(typeof currVal === "object") {
        return {
          basePath: acc.basePath,
          outputArr:  acc.outputArr.concat(this.myReducer(acc.basePath + '.' + currVal[0], currVal[1]))
        }
      }
      return acc
    }, {basePath: p, outputArr: []})
  }



  getArrayOfApplesValues = () => {
    const {
      applesObjects
    } = this.state

    if (applesObjects === null) return []

    Object.entries(applesObjects).reduce((acc, currVal, currIdx) => {
      if(typeof currVal[1] === "object") {
        return {
          path: acc.basePath,
          outputArr: acc.outputArr.concat(this.myReducer (acc.basePath + '.' + currVal[0], currVal[1]))
        }
      }
      return acc
    }, {basePath: '', outputArr: []})
  }

Простите, что в примерах мне не хватает знаний о яблоках.

Ответы [ 3 ]

2 голосов
/ 11 февраля 2020

@ mickl - отличный ответ, но вы можете попробовать это.

let fruits = {
  apples: {
    "colors": ["red", "green", "yellow"],
    "owner": "Person1",
    "types": [
      {name: "fuji", flavor: "good"}
    ]
  },
  pears: {
    "colors": ["red", "green", "yellow"],
    "owner": "Person1",
    "types": [
      {name: "small", flavor: "surprising"},
      {name: "large", flavor: "bold"}
    ]
  }
}
let arr = []
let fruitsArr = Object.entries(fruits)

fruitsArr.forEach(fruit => {
  let properties = fruit[1]
  let keys = Object.entries(fruit[1]).map(x => x[0])

  keys.forEach(key => {
    if (!Array.isArray(properties[key])) {
      return arr.push({
        path: `${fruit[0]}.${key}`,
        value: properties[key]
      })
    }

    if (typeof properties[key][0] === 'string') {
      properties[key].forEach(x => {
        return arr.push({
          path: `${fruit[0]}.${key}`,
          value: x
        })
      })
    }

    if (typeof properties[key][0] === 'object') {
      return arr.push({
        path: `${fruit[0]}.${key}`,
        value: properties[key][0]
      })
    }
  })
})
1 голос
/ 11 февраля 2020

Рекурсивный подход, используйте reduce и Object.etnries

const appleObjects = {
  apples: {
    colors: ["red", "green", "yellow"],
    owner: "Person1",
    types: [{ name: "fuji", flavor: "good" }]
  },
  pears: {
    colors: ["red", "green", "yellow"],
    owner: "Person1",
    types: [
      { name: "small", flavor: "surprising" },
      { name: "large", flavor: "bold" }
    ]
  }
};

const getKeysArr = (obj, prefix) => {
  var arr = Object.entries(obj).reduce((acc, [key, value]) => {
    const temp_key = prefix ? `${prefix}.${key}` : key;
    if (typeof value === "string" || typeof value === "number") {
      acc.push({ path: temp_key, value: value });
    } else if (Array.isArray(value)) {
      value.forEach(item => acc.push({ path: temp_key, value: item }));
    } else {
      acc.push(getKeysArr(value, key));
    }
    return acc;
  }, []);
  return arr;
};

console.log(getKeysArr(appleObjects, ""));
1 голос
/ 11 февраля 2020

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

let appleObjects = {
  'apples': {
    'colors': ['red', 'green', 'yellow'],
    'owner': 'Person1',
    'types': [
      {name: 'fuji', flavor: 'good'}
    ]
  },
  'pears': {
    'colors': ['red', 'green', 'yellow'],
    'owner': 'Person1',
    'types': [
      {name: 'small', flavor: 'surprising'},
      {name: 'large', flavor: 'bold'}
    ]
  }
};

let flatten = (obj, prefix, result) => {
    result = result || [];
    for(let key of Object.keys(obj)){
        let keyExpr = prefix  ? `${prefix}.${key}` : `${key}`;
        if(Array.isArray(obj[key])){
           obj[key].forEach(x => result.push({path: keyExpr, value: x}));
        }
        else if(typeof obj[key] === "object"){
            flatten(obj[key], keyExpr, result);
        }        
        else {
            result.push({path: keyExpr, value: obj[key]})
        }
    }
    return result;
}

let result = flatten(appleObjects);
console.log(result);
...