15
\$\begingroup\$

Description

Given an object (json), write code to extract all the keys from it. This is a question that I really wanted to ask everybody for a long time ago but I did not have time to write it up. It can be helpful in some cases in your daily work.

Rule:

  • You can use any parse function to get the JSON object, it does not cost you any bytes
  • Since a JSON object is a data structure that is not available in some languages, you can use any kind of data that has a similar structure in your favorite language.
  • To be clear, the input to your function should be a JSON object if it can.
  • The object can have nested keys.
  • The object can contain arrays and if it contains an array you will use the index of each element as a key.
  • Output can be a list with any format but it is preferred to be line by line.
  • The output keys can be arranged in any order, it does not matter.
  • Shortest bytes for each language will be win.
  • You can use any delimiter between each child key and its parent. Here I use . (please see the sample output for more detail).
  • The key can be a special character. For example: The input I got from @tsh
{"":{"":4,".":5},".":2,"..":3}

But this is a special case and it is not required to handle it. You are better to avoid it.

Example

Given an object:

A = {
"name" : {
  "first": "jane",
  "last": "doe"
},
"lang" : ["html", "css"]
}

Then the output should be:

"name"
"name.first"
"name.last"
"lang"
"lang.0"
"lang.1"

The index key (0 and 1) are a little tricky here, so for some languages like Javascript, it can be [0] and [1] instead.

So the example below is also correct:

"name"
"name.first"
"name.last"
"lang"
"lang[0]"
"lang[1]"

A Sample test case:

Input:

{
    "quiz": {
        "sport": {
            "q1": {
                "question": "Which one is a correct team name in the NBA?",
                "options": [
                    "New York Bulls",
                    "Los Angeles Kings",
                    "Golden State Warriors",
                    "Houston Rockets"
                ],
                "answer": "Houston Rockets"
            }
        },
        "maths": {
            "q1": {
                "question": "5 + 7 = ?",
                "options": [
                    "10",
                    "11",
                    "12",
                    "13"
                ],
                "answer": "12"
            },
            "q2": {
                "question": "12 - 8 = ?",
                "options": [
                    "1",
                    "2",
                    "3",
                    "4"
                ],
                "answer": "4"
            }
        }
    }
}

Output:

[
  "quiz",
  "quiz.sport",
  "quiz.sport.q1",
  "quiz.sport.q1.question",
  "quiz.sport.q1.options",
  "quiz.sport.q1.options.0",
  "quiz.sport.q1.options.1",
  "quiz.sport.q1.options.2",
  "quiz.sport.q1.options.3",
  "quiz.sport.q1.answer",
  "quiz.maths",
  "quiz.maths.q1",
  "quiz.maths.q1.question",
  "quiz.maths.q1.options",
  "quiz.maths.q1.options.0",
  "quiz.maths.q1.options.1",
  "quiz.maths.q1.options.2",
  "quiz.maths.q1.options.3",
  "quiz.maths.q1.answer",
  "quiz.maths.q2",
  "quiz.maths.q2.question",
  "quiz.maths.q2.options",
  "quiz.maths.q2.options.0",
  "quiz.maths.q2.options.1",
  "quiz.maths.q2.options.2",
  "quiz.maths.q2.options.3",
  "quiz.maths.q2.answer"
]

This is my solution using jq:

jq -r '[paths|map(.|tostring)|join(".")]'

Full code:

jq -r '[paths|map(.|tostring)|join(".")]' file.json

The content of file.json is an object from input

\$\endgroup\$
5
\$\begingroup\$

JavaScript (V8), 72 bytes

An edited version to support literal false, true and null values.

f=(o,s)=>!o|[o]==o||Object.keys(o).map(k=>f(o[k],k=s?s+[,k]:k,print(k)))

Try it online!


JavaScript (V8), 69 bytes

Takes a native JSON object as input. Prints the results, using a comma as the delimiter.

f=(o,s)=>[o]==o||Object.keys(o).map(k=>f(o[k],k=s?s+[,k]:k,print(k)))

Try it online!

How?

This is a recursive function walking through the keys at the root level and then in each sub-tree of the structure.

We need to process recursive calls on objects and arrays and to stop on strings and numbers. This is achieved with [o]==o||Object.keys(o):

 type of o | [o]==o    | Object.keys(o)  | string coercion example
-----------+-----------+-----------------+-------------------------------------
 array     | false     | 0-based indices | ['foo', 'bar'] -> 'foo,bar'
 object    | false     | native keys     | {abc: 'xyz'}   -> '[object Object]'
 string    | true      | n/a             | 'hello'        -> 'hello'
 number    | true      | n/a             | 123            -> '123'
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ fantastic, your answer beats @tsh, I am just curious what o.big is, could you please tell me about it? \$\endgroup\$ – Chau Giang Nov 8 '19 at 8:24
  • \$\begingroup\$ awesome, you rock! \$\endgroup\$ – Chau Giang Nov 8 '19 at 8:33
4
\$\begingroup\$

Ruby, 108 146 115 92 bytes

+38 bytes to fix test cases for objects inside arrays.....

-31 bytes because we can take a parsed JSON object as input now.

-17 bytes by removing the duplicated flat_map usage.

f=->j,x=''{z=j==[*j]?[*0...j.size]:j.keys rescue[];z.flat_map{|k|[r=x+k.to_s]+f[j[k],r+?.]}}

Try it online!

| improve this answer | |
\$\endgroup\$
3
\$\begingroup\$

Python 2, 122 135 122 119 bytes

f=lambda d:`d`[0]in'{['and sum([[str(k)]+['%s.'%k+q for q in f(v)]for k,v in(enumerate,dict.items)['{'<`d`](d)],[])or[]

Try it online!

Now handles an even broader class of inputs, including lists of dicts.

| improve this answer | |
\$\endgroup\$
3
\$\begingroup\$

JavaScript (Node.js), 75 bytes

f=o=>Object.keys(o+''===o||o||0).flatMap(k=>[k,...f(o[k]).map(i=>k+'.'+i)])

Try it online!

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Red, 159 bytes

func[s][r: :rejoin g: func[s m][foreach k keys-of m[print p:
r[s k]if map? t: m/:k[g r[p"."]t]if block? t[repeat n length?
t[print r[p"."n]]]]]g ""load-json s]

Doesn't work in TIO since load-json was introduced recently, but works fine in the Red console: enter image description here

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

R, 114 bytes

f=function(x){if(is.null(n<-names(x)))n<-seq(x);unlist(Map(function(y,z)c(z,if(is.list(y))paste(z,f(y))),x,n),,F)}

Try it online!

Defines a recursive function which takes an R list, possibly named, and returns the list of names using space as a separator. Here, a named list (or sub list) corresponds to an object in JSON, and an unnamed list corresponds to an array in JSON.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Wolfram Language (Mathematica), 39 27 bytes

MapIndexed[Echo@#2&,#,∞]&

Try it online! This function represents JSON arrays as Lists and objects as Associations. The function takes a JSON object as input and prints a set of part specifications to the standard output, each on their own line and preceded by >> . A part specification is a list of indices, where each index is a 1-based number or a string wrapped in Key. The index of a parent object is printed after those of its children. The output for the first example is:

>> {Key[name], Key[first]}
>> {Key[name], Key[last]}
>> {Key[name]}
>> {Key[lang], 1}
>> {Key[lang], 2}
>> {Key[lang]}

If the Key wrapper is undesirable, it can be removed using a 36-byte function:

Wolfram Language (Mathematica), 36 bytes

MapIndexed[Echo[#2/.Key->N]&,#,∞]&

The original challenge's formatting can be achieved with a 54-byte function:

Wolfram Language (Mathematica), 54 bytes

MapIndexed[Print@StringRiffle[#2/.Key->N,"."]&,#,∞]&
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

GO

//source is the json object and parent initially is ""
func getFieldName(source string,parent string)[]string{
    res := make([]string,0)
    var arr map[string]gjson.Result
    if gjson.Parse(source).IsObject(){
        arr = gjson.Parse(source).Map()
    }else{
        return  []string{parent}
    }
    for key,val := range arr{
        var temp []string
        if parent == ""{
            temp= getFieldName(val.Raw,key)
        }else{
            temp = getFieldName(val.Raw,parent + "." + key)
        }
        res = append(res,temp...)
    }
    return res
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Welcome to the site. Remember to add your score, and check out this page. \$\endgroup\$ – Calculuswhiz Aug 10 at 14:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.