Implement wildcard querySelector to extract URL
Company: Ramp
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Take-home Project
Quick Answer: This question evaluates implementation skills for a simplified selector engine, DOM traversal and manipulation, wildcard attribute pattern matching, and ordered string reconstruction from element attributes.
Part 1: Simplified Wildcard querySelectorAll
Constraints
- 0 <= number of nodes <= 10000
- If the tree is non-empty, 0 <= root < number of nodes
- `children` forms a valid rooted tree, and children are listed in document order
- Selector contains 1 to 20 space-separated segments, unless the tree is empty
- Each segment has optional tag, optional one class, and optional one attribute filter
- Attribute wildcard `*` matches any string, including empty
Examples
Input: (['document', 'section', 'article', 'div', 'b', 'b', 'span'], [[], [], [], [], ['ref'], ['ref'], ['ref']], [[], [['data-id', '23abc']], [['data-class', 'abc45']], [['data-tag', 'x78y']], [['value', 'h']], [['value', 'i']], [['value', '!']]], [[1], [2], [3], [4, 5, 6], [], [], []], 0, 'section[data-id="23*"] article[data-class="*45"] div[data-tag="*78*"] b.ref')
Expected Output: [4, 5]
Explanation: The section, article, and div match the wildcard attribute filters. Nodes 4 and 5 are matching b.ref descendants; node 6 has class ref but tag span.
Input: (['root', 'div', 'section', 'p', 'span', 'span'], [[], ['box'], [], [], ['target'], ['target']], [[], [['role', 'main']], [], [], [['data-code', 'AB']], [['data-code', 'CAB']]], [[1, 5], [2], [3], [4], [], []], 0, 'div.box span[data-code="*AB"]')
Expected Output: [4]
Explanation: Only node 4 is a descendant of the div.box node. Node 5 matches the segment but is not under that div.
Input: (['div', 'div', 'div', 'p'], [[], [], [], []], [[], [], [], []], [[1, 3], [2], [], []], 0, 'div div')
Expected Output: [1, 2]
Explanation: Node 1 is a div descendant of node 0. Node 2 is also a div with at least one div ancestor. Each result appears once.
Input: ([], [], [], [], -1, 'b.ref')
Expected Output: []
Explanation: An empty tree has no matching nodes.
Input: (['root', 'item', 'item'], [[], [], []], [[], [['data-id', '']], [['data-id', 'x']]], [[1, 2], [], []], 0, 'item[data-id="*"]')
Expected Output: [1, 2]
Explanation: The wildcard pattern * matches both the empty string and non-empty strings.
Hints
- Parse each selector segment into three checks: tag, class, and attribute pattern.
- During a preorder traversal, keep track of which selector prefixes have already matched among the current node's ancestors.
Part 2: Reconstruct Hidden URL from Matched DOM Nodes
Constraints
- 0 <= number of nodes <= 10000
- If the tree is non-empty, 0 <= root < number of nodes
- `children` forms a valid rooted tree, and children are listed in document order
- Selector contains 1 to 20 space-separated segments, unless the tree is empty
- Each selected URL character node usually has a `value` attribute containing one character
- Attribute wildcard `*` matches any string, including empty
Examples
Input: (['document', 'section', 'article', 'div', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b'], [[], [], [], [], ['other'], ['ref'], ['ref'], ['ref'], ['ref'], ['ref'], ['ref'], ['ref'], ['ref'], ['ref'], ['ref'], ['ref'], ['ref']], [[], [['data-id', '23abc']], [['data-class', 'z45']], [['data-tag', 'x78z']], [['value', '!']], [['value', 'h']], [['value', 't']], [['value', 't']], [['value', 'p']], [['value', 's']], [['value', ':']], [['value', '/']], [['value', '/']], [['value', 'x']], [['value', '.']], [['value', 'i']], [['value', 'o']]], [[1], [2], [3], [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []], 0, 'section[data-id="23*"] article[data-class="*45"] div[data-tag="*78*"] b.ref')
Expected Output: 'https://x.io'
Explanation: The decoy b node has class other and is ignored. The remaining matching b.ref nodes provide the URL characters in order.
Input: (['root', 'div', 'b', 'span', 'b', 'div', 'b', 'b'], [[], [], ['ref'], [], ['ref'], [], ['ref'], ['ref']], [[], [['data-kind', 'url-main']], [['value', 'g']], [], [['value', 'o']], [['data-kind', 'url-extra']], [['value', '.']], [['value', 'd']]], [[1, 5], [2, 3, 4], [], [], [], [6, 7], [], []], 0, 'div[data-kind="url*"] b.ref')
Expected Output: 'go.d'
Explanation: Both div containers match `url*`, so all b.ref descendants under them are concatenated in document order.
Input: (['b'], [['ref']], [[['value', 'Z']]], [[]], 0, 'b.ref')
Expected Output: 'Z'
Explanation: The root itself is allowed to match the first selector segment.
Input: (['root', 'div', 'b'], [[], [], ['other']], [[], [['data-kind', 'not-url']], [['value', 'x']]], [[1], [2], []], 0, 'div[data-kind="url*"] b.ref')
Expected Output: ''
Explanation: No container matches the required attribute pattern, so the reconstructed string is empty.
Input: ([], [], [], [], -1, 'b.ref')
Expected Output: ''
Explanation: An empty tree reconstructs to an empty string.
Hints
- First solve the selector-matching problem; reconstruction is then just a document-order concatenation.
- Use preorder traversal because document order for a tree is parent before children, with children visited from left to right.