INI sheet reader
MediumOnline Assessment
Read a hand-edited settings file in INI style and print its final settings in a sorted, canonical form.
The file is a list of lines. Go through them in order, keeping track of the current section (starting as the unnamed *global* section) and of the open key, the setting whose value can still be continued by following lines.
- Blank lines (empty or only whitespace) close the open key.
- Comment lines, whose first non-space character is
;or#, are ignored and do not close the open key. - Continuation lines: while a key is open, a line that starts with a space or tab and is not blank or a comment is appended to that key's value, as a single space followed by the line with surrounding whitespace removed (and its inline comment removed, see below). This applies even if the line looks like
name = value. - Any other line is first stripped of surrounding whitespace and of its inline comment: everything from the first
;that is *preceded by a space or tab* to the end (soa;bkeeps its;, and#never starts an inline comment). Then: [name]is a section header: the current section becomesname(surrounding spaces inside the brackets removed). Repeating a section name continues that same section.key = value(split at the first=) sets a key: the key and the value are stripped; the value may be empty. The key must not be empty. The new key becomes the open key.- Anything else (no
=, an empty key,[without a closing],[]) is malformed and ignored. - Section headers and malformed lines close the open key; a key line replaces it.
Section and key names are case-insensitive and reported in lowercase; values keep their case. Duplicate keys within a section are handled by dups: with "last" only the final assignment of a key is kept; with "list" every assignment is kept, in the order it appeared.
Input: lines, a list of strings; and dups, either "last" or "list".
Output: a list of strings "section.key=value" (just "key=value" for the global section), ordered by section name, with the global section first, then by key name, both in ascending lexicographic order. Entries of the same key (in "list" mode) stay in the order they were assigned. An empty file gives [].
Example 1
- Input:
- [["; app settings","name = Demo App","debug = yes ; temporary","[Server]","host = example.com","port = 80","port = 8080","paths = /a"," /b"," /c","[server]","Host = other.org"],"last"]
- Output:
- ["debug=yes","name=Demo App","server.host=other.org","server.paths=/a /b /c","server.port=8080"]
- Explanation
debug = yes ; temporaryloses its inline comment.[Server]and the later[server]are the same section, andHostis the same key ashost, so with"last"the final valueother.orgwins;portis 8080. The two indented lines continuepaths, which becomes/a /b /c. The global keys come first in the output.
Example 2
- Input:
- [["[tags]","t = red","t = green"," blue","# note inside"," dark","t =",""," stray","[Tags ]","t = last"],"list"]
- Output:
- ["tags.t=red","tags.t=green blue dark","tags.t=","tags.t=last"]
- Explanation
- In
"list"mode every assignment is kept.t = greenis continued byblueand, across a comment line (which does not close it), bydark. Thent =assigns an empty value, a blank line closes it, sostrayis a normal line with no=and is ignored.[Tags ]is the same section as[tags]and addslast.
Example 3
- Input:
- [["[Links]","url = http://x.org/a;b ; mirror"," retries = 3","broken line","[broken","=orphan","z = 1"],"last"]
- Output:
- ["links.url=http://x.org/a;b retries = 3","links.z=1"]
- Explanation
url = http://x.org/a;b ; mirrorkeeps the;insidea;b(no space before it) and drops the; mirrorcomment. The next line is indented whileurlis open, so it is a continuation even though it contains=: the value ishttp://x.org/a;b retries = 3.broken line,[brokenand=orphanare malformed and ignored, sozstill belongs to[links].
Constraints
0 <= lines.length <= 500; each line has at most 80 printable ASCII characters or tabsdupsis"last"or"list"- Lines are given without a trailing newline