Home > database >  How to gather nested object keys to an array?
How to gather nested object keys to an array?

Time:01-12

How can I convert object keys to an array with different data types? Javascript map function does not cover the object data type values.



Example:

let obj = {
  data1: 'test',
  data2: boolean,
  data3: {
    test1: 'test',
    test2: 'test'
  }
}

Expected output:

let arr = [
  'data1',
  'data2',
  'data3.test1',
  'data3.test2'
]

CodePudding user response:

const listPaths = (obj) => {
  function rKeys(o, path) {
    if (typeof o !== "object") return path;
    return Object.keys(o).map((key) =>
      rKeys(o[key], path ? [path, key].join(".") : key)
    );
  }

  return rKeys(obj).toString().split(",").filter(Boolean);
}

const obj = {
  data1: 'test',
  data2: false,
  data3: {
    test1: 'test',
    test2: 'test'
  }
};

console.log(listPaths(obj));

CodePudding user response:

Another potential option is to create a function transform to get the entries (a [[key, val], ...] array of your object) of your object and map each of those entries to its key if the value isn't an object, otherwise, if the value is an object, you can call the transform function again on the inner-object, which will again return an array of keys. You can then use .map() on this array of keys to add your current key (that holds the object) as a prefix to the keys from the inner object:

const obj = {
  data1: 'test',
  data2: true,
  data3: {
    test1: 'test',
    test2: {
      example1: 0
    },
    test3: 'test'
  }
};

const transform = (obj) => {
  return Object.entries(obj).flatMap(([key, val]) => 
    Object(val) === val ? transform(val).map(subKey => `${key}.${subKey}`) : key
  );
}

console.log(transform(obj));

CodePudding user response:

Here's a recursive approach to reach deep properties. Feel free to ask questions if something is quite difficult to understand.

const isArray = hit => Array.isArray(hit)
const isObject = hit => !isArray(hit) && typeof hit === 'object'
const isNonIterable = hit => !isArray(hit) && !isObject(hit)

const toPath = (data, prefix = '') => {
  const store = []

  if (isArray(data)) {
    if (prefix) {
      store.push(prefix)
    }

    store.push(...toPath(data[0], `${prefix}[i]`))
    return store
  }

  if (isObject(data)) {
    if (prefix) {
      store.push(prefix)
    }

    for (const key in data) {
      const currData = data[key]
      const currPrefix = prefix ? `${prefix}.${key}` : key

      if (isNonIterable(currData)) {
        store.push(currPrefix)
      } else {
        store.push(...toPath(currData, currPrefix))
      }
    }

    return store
  }

  return store
}


const data = {
  match: {
    id: 720359,
    play: '2021-12-20 00:30:00',
    score: null,
    status: 'Postponed'
  },
  league: {
    id: 1115,
    kor: '',
    name: 'Norway: Blno'
  },
  home_team: {
    id: 1610,
    kor: '',
    name: 'Tromso'
  },
  away_team: {
    id: 1613,
    kor: '',
    name: 'Avangard Omsk'
  },
  game: {
    id: 3,
    kor: '농구',
    name: 'basketball'
  },
  match_games: [
    {
      type: {
        id: 1,
        div: 'odd',
        kor: '승/무/패',
        name: '3Way Result'
      },
      odds: {
        odd: {
          size: 1,
          Home: 2.95,
          deep: 1,
          stop: 0,
          gap: 1.45,
          Draw: 13,
          Away: 1.5
        }
      },
      score: '[83,60]'
    }
  ]
}

const paths = toPath(data)

console.log(paths)

// [
//   '[i]',
//   '[i].match',
//   '[i].match.id',
//   '[i].match.play',
//   '[i].match.score',
//   '[i].match.status',
//   '[i].league',
//   '[i].league.id',
//   '[i].league.kor',
//   '[i].league.name',
//   '[i].home_team',
//   '[i].home_team.id',
//   '[i].home_team.kor',
//   '[i].home_team.name',
//   '[i].away_team',
//   '[i].away_team.id',
//   '[i].away_team.kor',
//   '[i].away_team.name',
//   '[i].game',
//   '[i].game.id',
//   '[i].game.kor',
//   '[i].game.name',
//   '[i].match_games',
//   '[i].match_games[i]',
//   '[i].match_games[i].type',
//   '[i].match_games[i].type.id',
//   '[i].match_games[i].type.div',
//   '[i].match_games[i].type.kor',
//   '[i].match_games[i].type.name',
//   '[i].match_games[i].odds',
//   '[i].match_games[i].odds.odd',
//   '[i].match_games[i].odds.odd.size',
//   '[i].match_games[i].odds.odd.Home',
//   '[i].match_games[i].odds.odd.deep',
//   '[i].match_games[i].odds.odd.stop',
//   '[i].match_games[i].odds.odd.gap',
//   '[i].match_games[i].odds.odd.Draw',
//   '[i].match_games[i].odds.odd.Away',
//   '[i].match_games[i].score'
// ]

Moreover, you can add the data type by just adjust the code a little bit:

const isArray = hit => Array.isArray(hit)
const isObject = hit => !isArray(hit) && typeof hit === 'object'
const isNonIterable = hit => !isArray(hit) && !isObject(hit)
const isBoolean = hit => typeof hit === 'boolean'
const isNumber = hit => typeof hit === 'number' || (!isNaN(Number(hit)) && !isBoolean(hit))
const isString = hit => typeof hit === 'string'
const isDate = hit => new Date(hit).toString() !== 'Invalid Date'

const getType = payload => {
  if (isArray(payload)) {
    return 'array'
  }

  if (isObject(payload)) {
    return 'object'
  }

  if (isNumber(payload)) {
    return 'number'
  }

  if (isDate(payload)) {
    return 'date'
  }

  if (isString(payload)) {
    return 'string'
  }

  if (isBoolean(payload)) {
    return 'boolean'
  }

  if (isBoolean(payload)) {
    return 'boolean'
  }
}

const toPath = (data, prefix = '') => {
  const store = []

  const storeData = (loggerPayload, loggerPrefix) => ({
    path: loggerPrefix,
    type: getType(loggerPayload)
  })

  if (isArray(data)) {
    if (prefix) {
      store.push(storeData(data, prefix))
    }

    store.push(...toPath(data[0], `${prefix}[i]`))
    return store
  }

  if (isObject(data)) {
    if (prefix) {
      store.push(storeData(data, prefix))
    }

    for (const key in data) {
      const currData = data[key]
      const currPrefix = prefix ? `${prefix}.${key}` : key

      isNonIterable(currData)
        ? store.push(storeData(currData, currPrefix))
        : store.push(...toPath(currData, currPrefix))
    }

    return store
  }

  return store
}

const data = [
  {
    match: {
      id: 720359,
      play: '2021-12-20 00:30:00',
      score: null,
      status: 'Postponed'
    },
    league: {
      id: 1115,
      kor: '',
      name: 'Norway: Blno'
    },
    home_team: {
      id: 1610,
      kor: '',
      name: 'Tromso'
    },
    away_team: {
      id: 1613,
      kor: '',
      name: 'Avangard Omsk'
    },
    game: {
      id: 3,
      kor: '농구',
      name: 'basketball'
    },
    match_games: [
      {
        type: {
          id: 1,
          div: 'odd',
          kor: '승/무/패',
          name: '3Way Result'
        },
        odds: {
          odd: {
            size: 1,
            Home: 2.95,
            deep: 1,
            stop: 0,
            gap: 1.45,
            Draw: 13,
            Away: 1.5
          }
        },
        score: '[83,60]'
      }
    ]
  }
]

const paths = toPath(data)

console.log(paths)

// [
//   {
//     "path": "[i]",
//     "type": "object"
//   },
//   {
//     "path": "[i].match",
//     "type": "object"
//   },
//   {
//     "path": "[i].match.id",
//     "type": "number"
//   },
//   {
//     "path": "[i].match.play",
//     "type": "date"
//   },
//   {
//     "path": "[i].match.score",
//     "type": "object"
//   },
//   {
//     "path": "[i].match.status",
//     "type": "string"
//   },
//   {
//     "path": "[i].league",
//     "type": "object"
//   },
//   {
//     "path": "[i].league.id",
//     "type": "number"
//   },
//   {
//     "path": "[i].league.kor",
//     "type": "number"
//   },
//   {
//     "path": "[i].league.name",
//     "type": "string"
//   },
//   {
//     "path": "[i].home_team",
//     "type": "object"
//   },
//   {
//     "path": "[i].home_team.id",
//     "type": "number"
//   },
//   {
//     "path": "[i].home_team.kor",
//     "type": "number"
//   },
//   {
//     "path": "[i].home_team.name",
//     "type": "string"
//   },
//   {
//     "path": "[i].away_team",
//     "type": "object"
//   },
//   {
//     "path": "[i].away_team.id",
//     "type": "number"
//   },
//   {
//     "path": "[i].away_team.kor",
//     "type": "number"
//   },
//   {
//     "path": "[i].away_team.name",
//     "type": "string"
//   },
//   {
//     "path": "[i].game",
//     "type": "object"
//   },
//   {
//     "path": "[i].game.id",
//     "type": "number"
//   },
//   {
//     "path": "[i].game.kor",
//     "type": "string"
//   },
//   {
//     "path": "[i].game.name",
//     "type": "string"
//   },
//   {
//     "path": "[i].match_games",
//     "type": "array"
//   },
//   {
//     "path": "[i].match_games[i]",
//     "type": "object"
//   },
//   {
//     "path": "[i].match_games[i].type",
//     "type": "object"
//   },
//   {
//     "path": "[i].match_games[i].type.id",
//     "type": "number"
//   },
//   {
//     "path": "[i].match_games[i].type.div",
//     "type": "string"
//   },
//   {
//     "path": "[i].match_games[i].type.kor",
//     "type": "string"
//   },
//   {
//     "path": "[i].match_games[i].type.name",
//     "type": "string"
//   },
//   {
//     "path": "[i].match_games[i].odds",
//     "type": "object"
//   },
//   {
//     "path": "[i].match_games[i].odds.odd",
//     "type": "object"
//   },
//   {
//     "path": "[i].match_games[i].odds.odd.size",
//     "type": "number"
//   },
//   {
//     "path": "[i].match_games[i].odds.odd.Home",
//     "type": "number"
//   },
//   {
//     "path": "[i].match_games[i].odds.odd.deep",
//     "type": "number"
//   },
//   {
//     "path": "[i].match_games[i].odds.odd.stop",
//     "type": "number"
//   },
//   {
//     "path": "[i].match_games[i].odds.odd.gap",
//     "type": "number"
//   },
//   {
//     "path": "[i].match_games[i].odds.odd.Draw",
//     "type": "number"
//   },
//   {
//     "path": "[i].match_games[i].odds.odd.Away",
//     "type": "number"
//   },
//   {
//     "path": "[i].match_games[i].score",
//     "type": "string"
//   }
// ]

CodePudding user response:

I'd suggest creating a function, say getKeys() that will traverse the object recursively, returning keys in the form 'a.b.c' once each leaf item is hit.

let obj = {
  data1: 'test',
  data2: true,
  data3: {
    test1: 'test',
    test2: 'test'
  }
}

function getKeys(obj, path = []) {
    let keys = [];
    // We've hit a 'leaf' in the object tree, return the path.
    if (!obj || typeof(obj) !== 'object') return [ path.join('.') ];
    // Call recursively for each child object...
    for(let k in obj) {
        keys = [...keys, ...getKeys(obj[k], [...path, k])];
    } 
    return keys;
}

console.log(getKeys(obj))
    
.as-console-wrapper { max-height: 100% !important; top: 0; }

CodePudding user response:

Create a recursive function that takes the object's entries, and iterates them with Array.flatMap(). Concat the current key to the previous one. If the current value is an object, call the function again, and pass the value, and the current key. If not return the current key.

This will work for multiple levels of nested objects.

const listPaths = (obj, init) => Object.entries(obj)
  .flatMap(([k, v]) => {
    const key = init ? `${init}.${k}` : k
    
    return typeof v === 'object' ? listPaths(v, key) : key
  })
 
const obj = {
  data1: 'test',
  data2: false,
  data3: {
    test1: 'test',
    test2: 'test',
    data3: {
      'sub-test1': 'sub-test',
      'sub-test2': 'sub-test'
    }
  }
}

const result = listPaths(obj)

console.log(result)

  •  Tags:  
  • Related