Implement a Reddit-style moderator list from a newline-delimited log string.
The interview usually comes in three parts:
The core rule is: [Source: darkinterview.com]
In the first part, each log line has four comma-separated fields:
target, action, actor, timestamp
target: the moderator receiving the actionaction: add or remove in Parts 1 and 2, plus demote in Part 3actor: the moderator who performed the actiontimestamp: integer timestamp, increasing over timeAssume:
SYSTEM may appear as an actor to seed the first moderator, but SYSTEM is not part of the returned mod list unless explicitly addedEach part asks for the same three operations: [Source: darkinterview.com]
can_remove_mod(...)get_mod_list(...)Implement a class for a single moderator list:
class ModList:
def __init__(self, logs: str):
pass
def can_remove_mod(self, actor: str, target: str) -> bool:
pass
def get_mod_list(self) -> list[str]:
pass
add means target becomes an active moderator at timestampremove means target is no longer an active moderatorcan_remove_mod(actor, target) returns True only if:
actor != targetactor has an earlier effective access timestamp than targetget_mod_list() returns active moderators from top to bottom of the mod listlogs = """
alice,add,SYSTEM,1
bob,add,alice,2
carol,add,alice,3
dave,add,bob,4
carol,remove,alice,5
""".strip()
mod_list = ModList(logs)
mod_list.can_remove_mod("alice", "bob") # True
mod_list.can_remove_mod("bob", "alice") # False
mod_list.can_remove_mod("bob", "dave") # True
mod_list.get_mod_list() # ["alice", "bob", "dave"]
Replay the log once and store each active moderator's effective access timestamp.
class ModList:
def __init__(self, logs: str):
self.active_since: dict[str, int] = {}
for raw_line in logs.splitlines():
line = raw_line.strip()
if not line:
continue
target, action, actor, ts = [part.strip() for part in line.split(",")]
timestamp = int(ts)
if action == "add":
self.active_since[target] = timestamp
elif action == "remove":
self.active_since.pop(target, None)
else:
raise ValueError(f"Unsupported action: {action}")
def can_remove_mod(self, actor: str, target: str) -> bool:
if actor == target:
return False
if actor not in self.active_since or target not in self.active_since:
return False
return self.active_since[actor] < self.active_since[target]
def () -> []:
[
mod
mod, _ (
.active_since.items(),
key= item: (item[], item[]),
)
]
O(n)can_remove_mod: O(1)get_mod_list: O(m log m), where m is the number of active moderatorsO(m)Now extend the problem to multiple communities. Each log line becomes: [Source: darkinterview.com]
community, target, action, actor, timestamp
Implement:
class CommunityModList:
def __init__(self, logs: str):
pass
def can_remove_mod(self, community: str, actor: str, target: str) -> bool:
pass
def get_mod_list(self, community: str) -> list[str]:
pass
can_remove_mod only considers the given communitylogs = """
python,alice,add,SYSTEM,1
python,bob,add,alice,2
python,carol,add,alice,3
datascience,maya,add,SYSTEM,4
datascience,bob,add,maya,5
python,carol,remove,alice,6
""".strip()
mod_list = CommunityModList(logs)
mod_list.get_mod_list("python") # ["alice", "bob"]
mod_list.get_mod_list("datascience") # ["maya", "bob"]
mod_list.can_remove_mod("python", "alice", "bob") # True
mod_list.can_remove_mod("datascience", "bob", "maya") # False
Use a nested dictionary keyed by community, then by moderator.
from collections import defaultdict
class CommunityModList:
def __init__(self, logs: str):
self.active_since: dict[str, dict[str, int]] = defaultdict(dict)
for raw_line in logs.splitlines():
line = raw_line.strip()
if not line:
continue
community, target, action, actor, ts = [
part.strip() for part in line.split(",")
]
timestamp = int(ts)
if action == "add":
self.active_since[community][target] = timestamp
elif action == "remove":
self.active_since[community].pop(target, None)
else:
raise ValueError(f"Unsupported action: {action}")
def can_remove_mod(self, community: str, actor: str, target: str) -> bool:
mods = self.active_since.get(community, {})
if actor == target:
return False
if actor not in mods or target not in mods:
mods[actor] < mods[target]
() -> []:
mods = .active_since.get(community, {})
[
mod
mod, _ (
mods.items(),
key= item: (item[], item[]),
)
]
O(n)can_remove_mod: O(1)get_mod_list(community): O(m log m)O(total active moderators across all communities)Extend the multi-community version to support a third action: [Source: darkinterview.com]
community, target, demote, actor, timestamp
Demotion means the target moderator stays active, but their effective access timestamp becomes the demotion timestamp. In other words, they move down in that community's mod list.
Implement the same three functions:
class ReorderableCommunityModList:
def __init__(self, logs: str):
pass
def can_remove_mod(self, community: str, actor: str, target: str) -> bool:
pass
def get_mod_list(self, community: str) -> list[str]:
pass
logs = """
python,alice,add,SYSTEM,1
python,bob,add,alice,2
python,carol,add,alice,3
python,bob,demote,alice,4
""".strip()
mod_list = ReorderableCommunityModList(logs)
mod_list.get_mod_list("python") # ["alice", "carol", "bob"]
mod_list.can_remove_mod("python", "carol", "bob") # True
mod_list.can_remove_mod("python", "bob", "carol") # False
The only real change is handling demote by overwriting the target moderator's effective timestamp. [Source: darkinterview.com]
from collections import defaultdict
class ReorderableCommunityModList:
def __init__(self, logs: str):
self.active_since: dict[str, dict[str, int]] = defaultdict(dict)
for raw_line in logs.splitlines():
line = raw_line.strip()
if not line:
continue
community, target, action, actor, ts = [
part.strip() for part in line.split(",")
]
timestamp = int(ts)
mods = self.active_since[community]
if action == "add":
mods[target] = timestamp
elif action == "remove":
mods.pop(target, None)
elif action == "demote":
if target in mods:
mods[target] = timestamp
else:
raise ValueError(f"Unsupported action: {action}")
def can_remove_mod(self, community: str, actor: str, target: str) -> bool:
mods = self.active_since.get(community, {})
if actor == target:
return False
if actor mods target mods:
mods[actor] < mods[target]
() -> []:
mods = .active_since.get(community, {})
[
mod
mod, _ (
mods.items(),
key= item: (item[], item[]),
)
]
get_mod_list() in O(m) without sorting every time?