Config path builder
MediumOnline Assessment
Build a nested settings document out of the flat path=value lines of an environment file.
Apply the assignments one by one, in order, to a document that starts as an empty object.
Path. A path is a key followed by any number of steps, each either .key (a field of an object) or [n] (an element of a list). A key is one or more of the characters A-Z a-z 0-9 _ -. An index n is 0, or a number from 1 to 99 without leading zeros. So a.b[2].c and grid[0][3] are valid, while x..y, [0]=1, k[01], k[100], k[a], a[0]b and paths with other characters are not. A line is split at its first =; everything after it is the value.
Rejected lines. A line with no = or with an invalid path is skipped and its position (0-based) is reported.
Value. true and false become booleans. 0, or an optional - followed by a number without leading zeros other than -0 itself, becomes an integer. Anything else, including the empty string, 007, -0, 1.5 and True, stays a string.
Assigning. Walk the path from the root, creating what is missing. A key step needs an object and an index step needs a list there; if the spot holds something else (a different kind of container, a string, a number, or null), it is replaced by a fresh empty object or list, and everything it contained is lost. A list that is too short is extended with null up to the index. The last step sets the value, replacing whatever was there.
Input: assignments, a list of strings.
Output: an object with two fields: "config", the final document, and "rejected", the positions of the skipped lines in increasing order.
Example 1
- Input:
- [["server.host=localhost","server.port=8080","server.tls=true","tags[0]=web","tags[2]=edge"]]
- Output:
- {"config":{"server":{"host":"localhost","port":8080,"tls":true},"tags":["web",null,"edge"]},"rejected":[]}
- Explanation
- Plain paths build nested objects.
tags[2]=edgeis stored at index 2 of the listtags, and the unfilled index 1 becomesnull(shown as None). Values are typed: 8080 is an integer,truea boolean, everything else a string.
Example 2
- Input:
- [["a=1","a.b=2","a.c[1]=x","a=hi","a.d=1","m[0]=x","m[0][1]=y"]]
- Output:
- {"config":{"a":{"d":1},"m":[[null,"y"]]},"rejected":[]}
- Explanation
- Later assignments win over whatever they find in the way.
a.b=2turns the integer a into an object,a=hithen replaces that whole object (and its c list) with a string, anda.d=1replaces the string with a fresh object holding only d. Inm[0][1]=ythe entry m[0], currently the string x, is replaced by a list.
Example 3
- Input:
- [["n=-12","bad","z=007","e=","eq=a=b","x..y=1","[0]=1","k[01]=1","k[100]=1","k[a]=1","t=True","f=false","ok-key_2.x[3][0]=7","neg=-0","a b=1"]]
- Output:
- {"config":{"n":-12,"z":"007","e":"","eq":"a=b","t":"True","f":false,"ok-key_2":{"x":[null,null,null,[7]]},"neg":"-0"},"rejected":[1,5,6,7,8,9,14]}
- Explanation
- Positions 1, 5, 6, 7, 8, 9 and 14 are rejected for: no
=, an empty key (x..y), a path starting with an index, an index with a leading zero, an index above 99, a non-numeric index, and a space inside the path.eq=a=bsplits at the first=only,z=007andneg=-0stay strings (leading zeros, minus zero), andt=Trueis a string because only lowercase true and false are booleans.
Constraints
0 <= assignments.length <= 200- Every assignment is a string of at most 60 characters; it may be malformed and then follows the rejection rule