Meta Data Engineer Interview Questions & Answers

meta icon

Questions with Detailed ExplanationsWith Detailed Explanations

(Last Updated: September 8, 2026)

11. Design the cloud data platform for an online gaming analytics workload.Cloud Data PlatformsHardMeta

Question Details

Select ingestion for gameplay and purchase events, durable raw storage, distributed transformation, session and match tables, historical warehouse or lakehouse storage, and low-latency leaderboards or operational aggregates. Include bursty launches, player and match keys, late results, replay, anti-duplication, schema evolution, access controls, workload isolation, and retention cost.

Short Interview Answer (30-60 seconds)

I would separate durable event capture from analytics serving: Kinesis absorbs bursty gameplay traffic, S3 keeps the replayable source of record, and Spark builds governed Iceberg tables. Redshift serves historical SQL, while Redis serves low-latency leaderboards, trading extra platform complexity for workload-specific performance and isolation.

Detailed Explanation

Game clients and servers continuously produce gameplay and purchase events, while analysts and product teams need both historical analysis and fast operational views such as leaderboards. One-off pipelines are not enough because launches can create sharp traffic bursts, events can arrive late or more than once, schemas evolve, and different workloads compete for compute. I would build a reusable event-to-analytics platform with a durable raw recovery boundary, distributed processing, governed lakehouse tables, separate historical SQL and low-latency serving paths, and cross-cutting access, observability, isolation, and retention controls.

Useful Questions to Ask the Interviewer
  1. How fresh must leaderboards and operational aggregates be compared with historical analytics?
  2. What ordering guarantees matter: per match, per player, or only eventual correctness after late results arrive?
  3. How long must raw gameplay and purchase events be retained before lifecycle policies move or delete them?
  4. Which datasets contain sensitive player or purchase information that needs finer access restrictions?
  5. How much isolation is required between BI, data-science, transformation, and operational serving workloads?
  6. Are schema changes expected to be backward compatible, or must the platform support controlled breaking changes?
Design the cloud data platform for an online gaming analytics workload. diagram
How to Explain It in an Interview
1. Start with the workload boundaries and correctness requirements

The platform has two producer types: game clients and game servers. They emit gameplay and purchase events. Each event carries event_id for deduplication, player_id and match_id for business grouping, event_time for event-time processing, and schema_version for evolution. The platform must absorb bursty launches without making downstream analytics systems the ingestion bottleneck. It must also preserve durable history so data can be replayed when transformation logic changes or downstream tables must be rebuilt.

I would treat the immutable S3 raw archive as the system of record. Kinesis is the near-real-time transport layer, not the final recovery boundary. If downstream processing fails, durable raw events remain available for replay and recomputation.

2. Ingest gameplay and purchase events with Amazon Kinesis Data Streams

Game clients and servers send near-real-time events into Amazon Kinesis Data Streams. The stream uses on-demand mode for variable traffic. Records use match_id or player_id as the partition key, depending on the required grouping and ordering scope. A match-oriented key is useful when events for the same match should stay together; a player-oriented key is useful for player-centric processing.

The architecture branches the Kinesis event flow toward Spark processing and toward durable raw S3 storage. For known launch spikes, the design allows pre-warming and requires throttled retries rather than uncontrolled producer retry storms.

A highly skewed partition key is an important ingestion risk. I would observe stream throughput, throttling, and consumer lag and revisit the key strategy if a small number of matches or players dominate traffic.

3. Keep an immutable Amazon S3 raw event archive for replay

Every ingested event is retained in Amazon S3 as immutable raw data. The archive is organized by date and hour and by event type, matching the design. It preserves the original event payload so a bad transformation does not destroy the recovery source.

This is the platform's replay boundary. If Spark logic is wrong, a curated table becomes incorrect, or a derived table needs to be rebuilt, Spark can read the raw S3 data and recompute the affected output. Replay must remain idempotent: event_id is used to identify duplicate business events so replay does not count the same event twice.

S3 lifecycle policies move older raw objects to lower-cost storage tiers as access frequency falls. This controls long-term retention cost without making the warehouse or Redis serving layer responsible for archival history.

4. Transform events with Apache Spark on Amazon EMR Serverless

Apache Spark running on Amazon EMR Serverless owns distributed transformation. It processes stream events during normal operation and can also process raw S3 data during replay or recomputation.

Spark deduplicates by event_id before events affect business aggregates. It uses event_time rather than arrival time for time-dependent calculations. Watermarks bound how long streaming state waits for late results. Malformed or invalid records are validated and quarantined instead of being silently published as trusted data.

The Spark jobs create and update curated session and match data. Session records contain player_id and session_id. Match records contain match_id and player_id. Late match results can update the appropriate logical record through the curated upsert or merge path rather than creating an unrelated duplicate result.

EMR Serverless automatically scales compute for variable processing demand. Separate EMR Serverless applications provide workload isolation where different teams or workload classes should not contend inside the same application boundary. Operators observe job failures, processing lag, malformed-event counts, and data-quality alerts.

5. Store curated history in an Apache Iceberg lakehouse on Amazon S3

Spark writes curated tables to Amazon S3 using Apache Iceberg. Iceberg provides the logical table layer over S3 files and supports schema evolution, snapshots, and time-travel access. AWS Glue Data Catalog stores catalog metadata for these tables; it does not store the production records themselves.

The key curated products shown in the architecture are sessions and matches. Sessions contain fields such as player_id and session_id. Matches contain match_id and player_id. These keys preserve the business grain required for gaming analytics and let later processing find the records affected by delayed results.

Schema evolution is handled at the Iceberg table boundary rather than by rewriting the raw archive. The schema_version field remains part of the producer event contract, Spark validates supported versions, and malformed or unsupported records should not be published as valid curated data.

Iceberg snapshots help operators inspect earlier table states after an incorrect write. The immutable raw S3 archive remains the deeper recovery source when a full recomputation is required.

6. Use Amazon Redshift Serverless for historical SQL analytics

Amazon Redshift Serverless is the SQL-oriented analytics serving layer for historical data and aggregates. Game analysts, product teams, and executives use it for BI-style analytical queries rather than sending complex historical queries to Redis or the ingestion stream.

The design uses separate Redshift workgroups for workload isolation, for example separating BI from data-science workloads. This reduces noisy-neighbor effects between those query classes and gives the platform distinct operational boundaries. Access to the underlying data is still governed through the platform's identity and permission controls.

If the Redshift analytics workload fails or becomes saturated, historical SQL consumers are affected, but Kinesis ingestion, raw S3 retention, Spark processing boundaries, and the replay source remain separate. That limits the blast radius.

7. Serve leaderboards and operational aggregates from Amazon ElastiCache for Redis

The low-latency path is separate from historical SQL. Spark computes real-time operational aggregates and writes them to Amazon ElastiCache for Redis. Redis stores leaderboard-oriented structures such as sorted sets for top-player rankings and other frequently read operational aggregates.

Redis is a serving layer, not the source of truth. Its contents must be reconstructable from durable data. If the cache loses state or an aggregate becomes incorrect, the affected leaderboard state is rebuilt from the durable lakehouse or raw-event history instead of treating Redis as authoritative.

Consumers that need fast leaderboard reads use Redis, while consumers that need historical analysis use Redshift or the governed lakehouse path. This keeps low-latency serving work away from historical analytical workloads.

8. Apply governance, metadata, and access controls across the platform

AWS IAM and AWS Lake Formation form the access-control boundary shown in the architecture. Identities receive least-privilege access to the systems and cataloged S3 data they need. Lake Formation provides fine-grained permissions on governed cataloged data, while IAM controls service and workload identities and their allowed actions.

AWS Glue Data Catalog owns table metadata. The platform also tracks schema management and evolution so producers and consumers can understand which contracts and table schemas are valid. Production records stay in the data plane; the catalog stores metadata about them.

Access rules should distinguish producer workloads, transformation jobs, BI users, and other consumers. Audit evidence comes from monitoring and logging rather than from assuming that catalog registration alone creates complete governance.

9. Observe quality, failures, cost, and workload isolation

Monitoring and logging cover the full architecture: ingestion pressure, Spark failures, replay activity, malformed records, data-quality alerts, warehouse workload behavior, Redis health, and downstream freshness. Lineage connects curated outputs back to upstream data so operators can identify affected consumers after a bad transformation or schema change.

The main correctness checks include duplicate detection, schema validation, freshness, completeness, and reconciliation after replay or recomputation. Restarting a failed task is not enough by itself; recovered outputs should be checked before they are treated as trusted again.

Cost attribution and retention policies are cross-cutting platform concerns. Raw S3 data moves to lower-cost storage tiers as it ages. EMR Serverless scales processing compute with workload demand. Separate EMR Serverless applications and separate Redshift workgroups provide explicit workload-isolation boundaries where contention matters.

10. Trace the normal path, failure path, and recovery path

The normal flow is: game clients and servers send events to Kinesis; the stream branches toward raw S3 storage and Spark; Spark validates, deduplicates, handles event time, and publishes curated Iceberg session and match tables; Spark also writes real-time aggregates to Redis; Redshift provides SQL analytics on historical data; BI and analytics users consume the historical analytics results while leaderboard consumers use the low-latency Redis serving layer.

For a Spark failure, durable raw data remains available for replay or recomputation. For duplicate delivery or replay, event_id identifies duplicate business events so the output logic can remain idempotent. For late match results, event-time handling and the curated merge path update the appropriate match or session state. For malformed data, validation and quarantine prevent incorrect records from entering trusted curated tables.

For a Redis failure, leaderboards can become temporarily unavailable or stale, but the serving state can be rebuilt from durable data. For a bad curated write, Iceberg snapshots support historical inspection and recovery of table state, while a complete recomputation can start from the raw S3 event archive.

11. Define ownership and reuse

Producer teams own correct gameplay and purchase event creation, including the agreed identifiers, event timestamp, and schema version. The shared data platform owns reusable ingestion, raw retention, Spark execution boundaries, catalog integration, governance controls, observability, replay mechanisms, and serving patterns. Data-product owners own the business meaning and validation of sessions, matches, and leaderboard or analytical aggregates.

This prevents each game or analytics team from rebuilding ingestion, storage, deduplication, access controls, replay, monitoring, and workload isolation independently. Domain-specific definitions, such as exactly how a session or leaderboard score is calculated, remain with the corresponding data-product owner.

12. Explain the main trade-offs

The first trade-off is simplicity versus workload fit. A single store would be easier to operate, but burst ingestion, durable retention, distributed transformation, historical SQL, and low-latency leaderboard reads have different needs. This architecture uses more components so those workloads can scale and fail independently.

The second trade-off is freshness versus cost. Streaming Spark processing and Redis provide fast operational results, while S3 carries durable history more economically. The platform therefore reserves the low-latency serving layer for operational aggregates instead of placing all retained history there.

The third trade-off is shared efficiency versus isolation. Shared serverless services can use capacity efficiently, while separate EMR Serverless applications and Redshift workgroups reduce contention between workload classes. Greater isolation creates more operational boundaries to manage and attribute.

The final trade-off is late-data correctness versus processing-state cost. A longer watermark accepts more delayed results inside the streaming path but holds event-time state longer. A shorter watermark reduces state but increases the chance that very late results require an explicit replay or correction path.

Technical Approach
  1. Define the event contract: event_id for deduplication, player_id and match_id for business grouping, event_time for late-data processing, and schema_version for evolution.
  2. Put Amazon Kinesis Data Streams at the near-real-time ingestion boundary and use match_id or player_id as the partition key according to the required ordering and grouping scope.
  3. Branch events to immutable Amazon S3 raw storage so replay and recomputation do not depend on downstream analytical or serving systems.
  4. Run Apache Spark on Amazon EMR Serverless for distributed stream processing and S3 replay. Validate records, deduplicate by event_id, use event-time watermarks, quarantine malformed data, and compute session, match, and operational aggregates.
  5. Publish curated session and match tables to Apache Iceberg on S3 and register their metadata in AWS Glue Data Catalog. Use Iceberg schema evolution and snapshots for controlled table evolution and historical inspection.
  6. Use Amazon Redshift Serverless for historical SQL analytics, with separate workgroups where BI and data-science workloads require isolation.
  7. Write low-latency leaderboard and operational aggregates to Amazon ElastiCache for Redis. Treat Redis as rebuildable serving state, not durable truth.
  8. Apply IAM and Lake Formation permissions to workloads and cataloged data. Track schema changes, lineage, data-quality alerts, cost attribution, and retention policies.
  9. Isolate transformation workloads with separate EMR Serverless applications where needed and observe ingestion pressure, processing state, data quality, warehouse behavior, cache health, and cost.
  10. Recover by replaying or recomputing from durable S3 data, use event_id to make business outcomes idempotent, and validate rebuilt outputs before republishing them.
Practical Insights

The first scaling pressure is ingestion during a game launch. Kinesis must distribute traffic across partition keys without allowing one very hot match or player key to dominate the stream. Spark compute grows with event throughput, transformation work, replay volume, and the amount of state retained for event-time processing. Longer watermarks can accept more late results but keep more streaming state.

S3 storage grows with retained raw events and curated lakehouse data, so lifecycle policies matter for old history. Iceberg metadata and table maintenance also grow as files, snapshots, and schema changes accumulate. Historical SQL cost depends on Redshift workload and concurrency, while Redis cost depends on how much low-latency aggregate state must stay available for serving.

Separate EMR Serverless applications and Redshift workgroups reduce noisy-neighbor risk but create more operational boundaries. Replay temporarily increases S3 reads and Spark compute, so operators should control replay concurrency rather than allowing recovery work to overwhelm normal processing.

There is no single big-O expression that describes the whole platform. In simple terms, event-processing work grows roughly with the amount of data processed, while retained storage grows with the amount of history kept by retention policy. The architecture deliberately spends extra platform complexity to separate burst ingestion, durable recovery, historical analytics, and low-latency serving.

Why Interviewers Ask This

This question tests whether a Data Engineer can turn different gaming workloads into one coherent platform. The important judgment is separating burst-tolerant event ingestion, durable event history, distributed transformation, historical analytics, and low-latency serving while still handling duplicates, late data, replay, schema changes, access control, noisy neighbors, and retention cost. It also tests whether the candidate understands that the raw S3 event archive is the recovery boundary and that a cache or warehouse should not become the only copy of important gameplay data.

Common interview mistakes

A common mistake is sending events directly to the warehouse and making it both the ingestion system and the recovery source. That makes burst handling, replay, and workload isolation harder. Another mistake is treating Redis as durable truth even though leaderboard state should be rebuildable from durable data.

Candidates also often say 'exactly once' without defining an end-to-end correctness boundary. This design instead uses event_id-based deduplication and idempotent replay behavior. Another mistake is using processing arrival time for match results and ignoring late events; the design uses event_time and watermarks, with replay or correction for results outside the normal streaming window.

Other mistakes include confusing AWS Glue Data Catalog metadata with production records, assuming Iceberg snapshots replace the immutable raw archive, ignoring producer schema compatibility because Iceberg supports schema evolution, or assuming a partition key provides complete workload or tenant isolation.

It is also weak to put every workload into one shared compute boundary. The architecture explicitly uses separate EMR Serverless applications and separate Redshift workgroups when stronger workload isolation is needed. Finally, candidates should not ignore retention cost: raw S3 history needs lifecycle policies, while Redis should contain only operational aggregates that benefit from low-latency serving.

Interview tip

Explain the design as four boundaries rather than as a vendor list: burst-tolerant ingestion, durable replayable truth, governed analytical processing and history, and workload-specific serving. Then trace one gameplay event end to end and explain duplicates, a late match result, a Spark failure, and a Redis rebuild.

Interviewer may ask next
What would you change if match results can arrive much later than normal gameplay events?

I would keep the same architecture but make late-result handling an explicit part of the match data product. Spark would continue to use event_time and a watermark for the normal streaming window. A late result that still falls inside that window updates the appropriate match record through the curated merge path. For results beyond the normal watermark, I would use a controlled replay or correction process rather than keeping streaming state open indefinitely. The raw S3 archive remains the recovery source, and event_id keeps the correction idempotent. After recomputation, I would validate the affected match and dependent aggregates before publishing them again. The trade-off is that a longer watermark handles more delayed events automatically but retains more state, while a shorter watermark reduces state at the cost of more explicit correction work.

How would you protect the platform if a major game launch causes ingestion and analytics demand to spike at the same time?

I would preserve the same service boundaries so the spike does not become one shared failure domain. Kinesis remains the near-real-time ingestion boundary, with monitoring for throttling, lag, and hot partition keys. The raw S3 branch continues to protect durable events even if downstream transformation falls behind. EMR Serverless scales transformation compute, while separate EMR Serverless applications prevent unrelated workload classes from sharing one application boundary. Historical BI and data-science activity remains isolated through separate Redshift workgroups, and Redis serves leaderboards without forcing those reads onto the historical analytics path. If processing lag grows, I would prioritize durable ingestion and raw capture first, then let Spark catch up or replay from S3. The trade-off is cost and operational complexity: stronger isolation reduces noisy-neighbor risk during launches but creates more resources and spending boundaries to manage.

12. Measure the performance difference between a logical view and a materialized result.PerformanceEasyMeta

Question Details

Choose a recurring analytical query and compare plan expansion, base-table scans, predicate pushdown, caching, refresh work, storage, and concurrency for a logical view versus a materialized table or view. Require before-and-after latency and bytes or rows processed while verifying that freshness and result values remain acceptable.

Short Interview Answer (30-60 seconds)

I would run the same recurring analytical query under controlled conditions against the logical view and the materialized result, compare their physical plans, latency, and rows or bytes processed, and account for caches and concurrency. I would use materialization only when the measured read benefit justifies refresh work, storage, and freshness trade-offs.

Detailed Explanation

A logical view normally stores a query definition, so when it is queried the optimizer can expand that definition, build a physical execution plan, and read the required base tables. A materialized table or view stores computed results, so the recurring query may read that stored result instead of repeating the full base-table work. To compare them fairly, I would use the same analytical query and representative data, measure latency and rows or bytes processed, inspect the physical plan, control caching and concurrency, and verify that both result values and freshness remain acceptable.

Useful Questions to Ask the Interviewer
  1. Is the analytical query executed frequently enough that avoiding repeated base-table work could matter?
  2. What freshness requirement must the materialized result meet, and how much delay is acceptable?
  3. Should I compare cold-cache behavior, warm-cache behavior, or both?
  4. What concurrency level should remain constant during the comparison?
  5. Does the platform expose physical plans and scan metrics such as rows or bytes processed?
Measure the performance difference between a logical view and a materialized result. diagram
How to Explain It in an Interview

I would start with one recurring analytical query and use that exact workload for both measurements. In the baseline, the query reads through the logical view. The view definition is expanded into the optimizer's planning process, which produces a physical plan that may scan the required fact, dimension, and other base tables. I would inspect the executed physical plan or scan metrics rather than assuming that a filter appearing in the logical query was physically pushed down. Predicate, projection, or partition pruning counts only when the physical plan or scan evidence confirms it.

For the baseline, I would record end-to-end latency and rows or bytes processed. I would also control or explicitly record cache state because a warm result cache can make a query appear faster without proving that its underlying execution plan became cheaper. I would keep concurrency comparable so queueing or competition from other queries does not distort the comparison.

Next, I would persist the recurring query result as a materialized table or materialized view. This is the single targeted change. It adds stored data that the recurring query can read directly, but it also introduces additional storage and periodic or on-demand refresh work. The refresh process must incorporate new source data often enough to satisfy the required freshness window.

I would then rerun the same analytical query against the materialized result under the same workload, cache policy, and concurrency conditions. I would again record latency and rows or bytes processed. The expected mechanism is less repeated computation and, when the physical execution confirms it, less data processing than repeatedly expanding the logical view and scanning its underlying tables. I would not claim an improvement merely because a result happened to be cached.

Finally, I would compare the before-and-after measurements and validate correctness. The query logic and filters must remain equivalent. Result values should match for the same accepted snapshot or freshness contract, and the materialized result must remain within the allowed freshness window. If a refresh is late or fails and freshness is no longer acceptable, I would treat the materialized result as failing the correctness contract for this use case rather than counting its lower latency as a successful optimization.

The decision is therefore measurement-driven. If the materialized result reduces latency and processed data enough to justify its storage, refresh computation, operational maintenance, and possible freshness lag, it is a good fit for this recurring analytical workload. If the query is rarely reused, source data changes too frequently, or the freshness requirement is too strict, the logical view may remain the better choice.

Technical Approach
  1. Choose one recurring analytical query and define its correctness and freshness requirements.
  2. Run it through the logical view and inspect the executed physical plan.
  3. Record baseline latency and rows or bytes processed.
  4. Verify actual predicate, projection, or partition pruning from the physical plan or scan metrics.
  5. Control or record cache state and keep concurrency comparable.
  6. Persist the same recurring result as a materialized table or materialized view.
  7. Define periodic or on-demand refresh work that keeps the stored result within the accepted freshness window.
  8. Rerun the same analytical query against the materialized result under the same test conditions.
  9. Record after-change latency and rows or bytes processed.
  10. Validate equivalent query logic, acceptable result values, and freshness.
  11. Decide whether the measured read-time benefit is worth the additional storage, refresh work, operational maintenance, and possible freshness lag.
Practical Insights

The logical view does not require storing another copy of the computed result, but each recurring execution may repeat planning and base-table reads, consuming CPU, storage I/O, network capacity, and worker time. A materialized result consumes persistent storage and requires refresh computation, but repeated reads can avoid some recomputation and may process fewer rows or bytes. Refreshes themselves consume resources and can compete with queries, so concurrency must be measured rather than assumed. Cache state also matters because a cached result can reduce observed latency without proving that the underlying plan became cheaper. The practical trade-off is faster repeated reads versus storage, refresh cost, operational work, and freshness risk.

Why Interviewers Ask This

Interviewers want to see whether I can distinguish a stored query definition from stored query results and measure the real execution cost instead of assuming materialization is automatically faster. A strong answer compares plan expansion, base-table scans, verified predicate or projection pruning, cache effects, refresh work, storage, concurrency, latency, and rows or bytes processed while preserving result correctness and acceptable freshness.

Common interview mistakes

Common mistakes include assuming that a logical filter proves physical predicate pushdown, comparing a cold logical-view run with a warm cached materialized run, changing the query or data between tests, inventing benchmark numbers, measuring only latency while ignoring rows or bytes processed, ignoring concurrency, and treating result-cache reuse as proof of a faster execution plan. Another mistake is reporting only the materialized query's read-time benefit while ignoring storage and refresh work. The most serious correctness error is accepting stale materialized values when they fall outside the agreed freshness window.

Interview tip

Present this as a controlled before-and-after experiment: same recurring query, representative data, comparable cache and concurrency conditions, physical-plan evidence, latency plus rows or bytes processed, then correctness and freshness validation. Finish by explaining that materialization is worthwhile only when its measured read benefit exceeds its storage, refresh, and operational costs.

Interviewer may ask next
How would you make sure the benchmark is fair if caching can affect both versions?

I would explicitly control or record cache state and compare equivalent conditions. I could run separate cold-cache and warm-cache tests when relevant, repeat runs when variability matters, and keep the query, representative data, concurrency, and measurement boundary unchanged. I would also inspect the physical plan and scan metrics because low latency from a cached result does not prove that the underlying execution processes less data.

When would you keep the logical view even if the materialized result has lower query latency?

I would keep the logical view when the materialized result's refresh work, storage, maintenance, or freshness lag is not justified by the measured read-time benefit. Examples include infrequently reused queries, rapidly changing data with strict freshness requirements, or workloads where the logical-view plan already prunes and scans little data. The choice should consider both query-time performance and the ongoing cost and correctness implications of maintaining the stored result.

13. Join two lists and return all values in sorted order.CodingEasyMeta

Question Details

Implement mergeAndSort(first, second). Concatenate the two input lists, preserve every occurrence including duplicates, and return one list in nondecreasing order; either input may be empty. Example: first=[4,-1,4], second=[3,0] returns [-1,0,3,4,4].

Short Interview Answer (30-60 seconds)

I would concatenate the two input lists so every value and duplicate is preserved, then sort the combined list in nondecreasing order and return it. In Python, I can do this directly with sorted(first + second). For first = [4, -1, 4] and second = [3, 0], the combined list is [4, -1, 4, 3, 0], and the result is [-1, 0, 3, 4, 4]. The time complexity is O((n + m) log(n + m)), with O(n + m) auxiliary space.

Detailed Explanation

See the Code while reading this explanation.

The task gives me two lists of values. I need to put all values from both lists into one list, keep repeated values, and return the values from smallest to largest. Either input list may be empty. The direct approach is to concatenate the lists first and then sort all of the combined values. This matches the required output and keeps duplicates. For the example, [4, -1, 4] and [3, 0] become [4, -1, 4, 3, 0], which sorts to [-1, 0, 3, 4, 4].

Useful Questions to Ask the Interviewer
  1. Should the original input lists remain unchanged?
  2. Can I assume the list values can be compared with each other for sorting?
Join two lists and return all values in sorted order. diagram
How to Explain It in an Interview
1. Understand the input and required output

The function receives two lists named first and second. It must return one new list. Every occurrence from both inputs must appear in the result, including duplicates. The result must be in nondecreasing order. Either input may be empty.

2. Concatenate the two lists

For the example, first is [4, -1, 4] and second is [3, 0]. Concatenating them gives [4, -1, 4, 3, 0]. This step keeps all five input occurrences. The value 4 still appears twice because it appeared twice in the first input.

3. Sort the combined list

Next, sorted(...) orders [4, -1, 4, 3, 0] into nondecreasing order. The result is [-1, 0, 3, 4, 4]. Sorting changes the order of the values, but it does not remove duplicates.

4. Return the final list

The function returns [-1, 0, 3, 4, 4]. All five original occurrences are present in the output. Both copies of 4 remain.

5. Explain why the result is correct

The central invariant is that every original occurrence must appear exactly once in the result. Concatenation preserves all occurrences from both lists. Sorting then puts those same occurrences in nondecreasing order without removing any values. This satisfies both parts of the required contract.

6. Explain the Python implementation and complexity

The implementation uses sorted(first + second). The expression first + second creates the combined list. sorted(...) returns a new sorted list. If n is len(first) and m is len(second), sorting n + m values takes O((n + m) log(n + m)) time. The temporary combined list and returned sorted list both grow with n + m, so the auxiliary space is O(n + m), and the returned output itself contains O(n + m) values.

Key Insight / Why This Solution Works

The solution uses concatenation followed by sorting. First, first + second creates one list containing every occurrence from both inputs. Then sorted(...) orders that complete list in nondecreasing order. The central invariant is that every original occurrence appears exactly once in the result. Concatenation establishes this because it keeps every element, including duplicates. Sorting changes only the order, not the number of occurrences. Therefore the returned list contains exactly the input values with the required ordering.

Code
def mergeAndSort(first: list[int], second: list[int]) -> list[int]:
    # Concatenate both inputs so every occurrence is preserved, then return a new sorted list.
    return sorted(first + second)


def main() -> None:
    # Use the exact example from the problem and diagram.
    first = [4, -1, 4]
    second = [3, 0]

    # Run the function and display the expected sorted result.
    result = mergeAndSort(first, second)
    print(result)


if __name__ == "__main__":
    # Run the example only when this file is executed directly.
    main()
Time & Space Complexity

Let n be len(first) and m be len(second). Concatenating the two lists takes O(n + m) time. Sorting the combined n + m values takes O((n + m) log(n + m)) time, so that is the overall time complexity. The concatenated temporary list grows with n + m, and sorted(...) creates the returned sorted list. The auxiliary space is O(n + m). The output list also contains n + m values, so its output space is O(n + m).

Where it is used

This pattern is useful when two batches of comparable values must be combined into one ordered result while keeping repeated values. Examples include combining two collected sets of measurements, scores, timestamps, or numeric records before producing an ordered report.

Why Interviewers Ask This

This question checks whether a candidate can turn a simple data contract into correct code without adding unnecessary complexity. The interviewer can evaluate whether the candidate preserves duplicates, handles empty inputs, distinguishes concatenation from sorting, and explains why the result is correct. It also tests whether the candidate can use an appropriate built-in operation and state the sorting time and memory costs accurately.

Common interview mistakes
  1. Using a set before sorting. A set would remove duplicates, so one of the two 4 values would be lost.
  2. Returning first + second without sorting it. [4, -1, 4, 3, 0] preserves the values but is not in nondecreasing order.
  3. Forgetting that either input may be empty. The same concatenation-and-sort logic still works when first, second, or both are empty.
  4. Claiming the solution runs in O(n + m) time. The sorting step makes the overall time O((n + m) log(n + m)).
  5. Using an in-place sort on one input when the shown solution returns a new sorted list.
Interview tip

Explain the two required properties before writing code: every occurrence must be preserved, and the final values must be sorted. Then show that first + second preserves all five example values and sorted(...) changes [4, -1, 4, 3, 0] into [-1, 0, 3, 4, 4].

Interviewer may ask next
What would change if both input lists were already sorted?

I could replace the full sorting step with a two-pointer merge. I would compare the current value from each sorted list, append the smaller value, and advance that pointer. When one list is exhausted, I would append the remaining values from the other list. This preserves every occurrence and keeps the output sorted. The new time complexity would be O(n + m), and the returned output would use O(n + m) space. The tradeoff is that this faster method depends on both inputs already being sorted.

Does the shown solution modify either input list?

No. first + second creates a new combined list, and sorted(...) returns a new sorted list. The original first and second lists remain unchanged. The algorithm still takes O((n + m) log(n + m)) time and O(n + m) auxiliary space.

14. Validate whether a string is a correctly formatted IPv4 or IPv6 address.CodingMediumMeta

Question Details

Implement isValidIPAddress(value). Accept IPv4 only when it has four decimal components from 0 through 255, no empty component, no sign, and no leading zero on a multi-digit component. Accept IPv6 only when it has eight non-empty hexadecimal groups of one to four characters. Return false for every other form. Examples: "172.16.254.1" and "2001:db8:85a3:0:0:8A2E:0370:7334" return true, while "256.1.1.1" and "2001:db8::1" return false under this full-form contract.

Short Interview Answer (30-60 seconds)

I first use the delimiter to decide which validation rules apply. A dot-only value goes through IPv4 validation, while a colon-only value goes through IPv6 validation. IPv4 must contain exactly four ASCII-decimal components from 0 to 255, with no empty part, sign, or leading zero on a multi-digit part. IPv6 must contain exactly eight non-empty hexadecimal groups of one to four characters. I stop on the first invalid condition. The solution takes O(n) time and O(n) auxiliary space.

Detailed Explanation

See the Code while reading this explanation.

The input is one value that should represent either an IPv4 or IPv6 address. The function returns true only when the whole value follows one of the two exact formats. IPv4 needs four decimal parts separated by dots. IPv6 needs eight hexadecimal groups separated by colons. This problem intentionally accepts only the full IPv6 form, so compressed notation such as 2001:db8::1 is invalid. The main idea is to choose the format from the delimiter, split the value, and then validate every component using that format's rules.

Useful Questions to Ask the Interviewer
  1. Should compressed IPv6 notation such as 2001:db8::1 be rejected? Under this contract, yes.
  2. Should an IPv4 component such as 01 be rejected because it has a leading zero? Under this contract, yes.
  3. Should IPv6 hexadecimal letters be accepted in both uppercase and lowercase? Yes.
Validate whether a string is a correctly formatted IPv4 or IPv6 address. diagram
How to Explain It in an Interview
1. Choose the address type from the delimiter

The function starts with the input value. If the value is empty, it returns false. If the value contains a dot and no colon, it follows the IPv4 path. If it contains a colon and no dot, it follows the IPv6 path. If it contains both delimiter types, or neither one, it returns false. This keeps the two validation rules separate.

2. Validate the IPv4 structure and components

For IPv4, split the value on dots. There must be exactly four components. Because Python split with an explicit separator keeps empty fields, malformed values such as 1..1.1 are rejected. Each component must contain only ASCII digits from 0 through 9. A multi-digit component cannot start with 0. A component longer than three digits is invalid because it cannot be within 0 through 255. After those checks, convert the component to an integer and require the value to be between 0 and 255 inclusive.

3. Validate the IPv6 structure and groups

For IPv6, split the value on colons. There must be exactly eight groups. Every group must be non-empty and contain from one through four characters. Every character must be a hexadecimal digit: 0-9, a-f, or A-F. Because all eight groups must be present and non-empty, compressed notation containing :: is rejected under this full-form contract.

4. Walk through the verified example

The diagram uses 172.16.254.1. It contains dots and no colons, so the IPv4 branch is selected. Splitting gives ["172", "16", "254", "1"]. There are exactly four components. "172" contains only ASCII digits, has no leading zero, and has value 172, which is within 0 through 255. The same checks pass for "16", "254", and "1". All four components are valid, so the function returns true and stops.

5. Explain why the algorithm is correct

The invariant is that every component already processed satisfies every rule for the selected address format. For IPv4, true is returned only after exactly four valid decimal components pass. For IPv6, true is returned only after exactly eight valid hexadecimal groups pass. Any failed structure, character, length, leading-zero, or range check returns false immediately. Therefore an accepted value satisfies the complete required contract.

6. Explain the implementation, complexity, and edge cases

The Python code follows the same order as the diagram: inspect delimiters, split with the correct separator, verify the required component count, validate each component, and return true only after all checks pass. The running time is O(n), where n is the input length. The split operation creates component strings, so auxiliary space is O(n). Important rejected cases include 256.1.1.1, 01.2.3.4, 1..1.1, signed IPv4 components, the wrong number of components or groups, non-hexadecimal IPv6 characters, groups longer than four characters, and compressed IPv6 such as 2001:db8::1.

Key Insight / Why This Solution Works

The key idea is to separate format detection from token validation. First, the delimiter decides which validation path is allowed. A value with dots and no colons is checked as IPv4. A value with colons and no dots is checked as IPv6. Any other delimiter pattern is rejected. The central invariant is that every component already processed satisfies all rules for the selected format. The algorithm returns false as soon as that invariant would be broken. It returns true only after every required component or group passes, which matches the strict contract shown in the diagram.

Code
def isValidIPAddress(value: str | None) -> bool:
    # Reject a missing or empty value before selecting an address format.
    if value is None or value == "":
        return False

    # A dot-only delimiter pattern selects strict IPv4 validation.
    if "." in value and ":" not in value:
        return isValidIPv4(value)

    # A colon-only delimiter pattern selects strict full-form IPv6 validation.
    if ":" in value and "." not in value:
        return isValidIPv6(value)

    # Mixed delimiters, or no recognized delimiter, are outside the contract.
    return False


def isValidIPv4(value: str) -> bool:
    # Splitting with an explicit separator preserves empty components.
    parts = value.split(".")
    if len(parts) != 4:
        return False

    for part in parts:
        # Every component must be non-empty and contain only ASCII decimal digits.
        if not part or not all("0" <= ch <= "9" for ch in part):
            return False

        # Multi-digit components cannot have a leading zero.
        if len(part) > 1 and part[0] == "0":
            return False

        # More than three decimal digits can never represent a value from 0 through 255.
        if len(part) > 3:
            return False

        # Convert only after syntax checks, then enforce the numeric range.
        number = int(part)
        if number < 0 or number > 255:
            return False

    # All four components satisfied every IPv4 rule.
    return True


def isValidIPv6(value: str) -> bool:
    # Empty groups are preserved, so compressed forms containing :: are rejected.
    groups = value.split(":")
    if len(groups) != 8:
        return False

    hexadecimal_digits = set("0123456789abcdefABCDEF")

    for group in groups:
        # Every full-form IPv6 group must contain one through four characters.
        if not 1 <= len(group) <= 4:
            return False

        # Every character must be a valid uppercase or lowercase hexadecimal digit.
        if not all(ch in hexadecimal_digits for ch in group):
            return False

    # All eight groups satisfied every strict full-form IPv6 rule.
    return True


def main() -> None:
    # Run the same verified example shown in the diagram.
    value = "172.16.254.1"
    print(isValidIPAddress(value))


if __name__ == "__main__":
    main()
Time & Space Complexity

Let n be the number of characters in the input. The algorithm takes O(n) time because delimiter checks, splitting, and component validation process an amount of text proportional to the input length. It may stop early when it finds an invalid condition. The illustrated split-based implementation uses O(n) auxiliary space because split creates a list of component strings whose total size grows with the input. An on-the-fly parser could use O(1) auxiliary space, but that is not the implementation shown in the diagram.

Where it is used

This validation pattern is useful when a system must reject malformed network-address fields before using or storing them. A Data Engineer could apply the same pattern while validating configuration records, cleaning imported datasets, checking network fields in logs, or rejecting malformed records before they move to later pipeline stages.

Why Interviewers Ask This

This question tests whether you can translate a precise string contract into correct validation logic. The interviewer can evaluate how carefully you handle delimiters, token counts, empty fields, numeric ranges, leading zeros, character sets, and early failure. It also checks whether you notice that this problem intentionally uses stricter IPv6 rules than general compressed IPv6 notation. Finally, it tests whether your code, example, complexity explanation, and edge-case reasoning stay consistent.

Common interview mistakes

A common mistake is converting an IPv4 component to an integer before checking its exact text format. That can hide invalid signs or leading zeros. Another mistake is checking only the numeric range while forgetting the exact count of four components or empty components. For IPv6, candidates may accidentally accept compressed notation with :: even though this problem requires eight non-empty groups. It is also easy to forget uppercase hexadecimal letters or to allow groups longer than four characters. Another subtle mistake is using a broad Unicode digit test when the contract requires ASCII digits 0-9.

Interview tip

State the validation contract before writing code: dot-only selects IPv4, colon-only selects strict full-form IPv6, and any other delimiter pattern is invalid. Then perform structural checks before numeric or character checks. This order makes the solution easy to explain and prevents malformed inputs from reaching later validation steps.

Interviewer may ask next
How would the solution change if compressed IPv6 forms such as 2001:db8::1 also had to be accepted?

The IPv4 path would stay the same. The IPv6 validator would allow at most one :: marker. Groups before and after it would still need one through four hexadecimal characters. If :: is present, the number of explicit groups must be fewer than eight, and the omitted positions represent enough zero groups to reach eight total groups. Without ::, exactly eight groups are still required. The time complexity remains O(n). A split-based implementation still uses O(n) auxiliary space. The tradeoff is more complex validation logic.

Can the auxiliary space be reduced?

Yes. Instead of splitting the whole value, the function could scan the original string and validate one component at a time between delimiters. It would keep only the current component length, IPv4 numeric value when needed, component count, and a few flags. That preserves O(n) time and reduces auxiliary space to O(1). The tradeoff is more manual parsing and more code than the clear split-based approach shown in the diagram.

15. Deduplicate click events while keeping the earliest timestamp for each user-event key.CodingHardMeta

Question Details

Implement dedupeEventsKeepEarliest(events). Each event is a dictionary containing user_id, event_id, and an ISO-8601 ts. For every repeated (user_id,event_id), keep only the record with the earliest timestamp; return retained records in the relative order of their retained source positions, even when an earlier duplicate appears later in the input. Example: [(u1,e1,12:05),(u2,e2,12:02),(u1,e1,12:01)] returns the u2 record followed by the 12:01 u1 record.

Short Interview Answer (30-60 seconds)

I would use a hash map keyed by (user_id, event_id). While I scan the events in source order, I parse each timestamp and keep the event when its key is new or its timestamp is strictly earlier than the stored one. I also store the winning source index. After the scan, I sort the retained winners by that index and return their events. The expected time is O(n + k log k), and the auxiliary space is O(k).

Detailed Explanation

See the Code while reading this explanation.

The input is a list of click-event records. Each record has a user ID, an event ID, and a timestamp. The same user-event pair can appear more than once. We keep the record with the earliest timestamp for each pair. The important detail is the output order. We do not return records in timestamp order. We return the winning records in the relative order of the source positions where those winning records appeared.

The solution uses a hash map. For each (user_id, event_id) key, it stores the winning event, its parsed timestamp, and its source index. This lets a later record replace an earlier source record when its timestamp is earlier.

Useful Questions to Ask the Interviewer
  1. If two records for the same key have equal timestamps, should the first source occurrence be kept? The shown solution does this because it replaces only for a strictly earlier timestamp.
  2. Can I assume every ts value is a valid ISO-8601 timestamp string that datetime.fromisoformat() can parse?
  3. Should the function return the original retained event dictionaries rather than copies?
Deduplicate click events while keeping the earliest timestamp for each user-event key. diagram
How to Explain It in an Interview
1. Understand the input and required output

Each input event is a dictionary containing user_id, event_id, and ts. The deduplication key is (user_id, event_id). For every repeated key, we keep the record with the earliest timestamp. The returned records must follow the relative order of their retained source positions.

For the diagram example:

  • Index 0: (u1, e1, 12:05)
  • Index 1: (u2, e2, 12:02)
  • Index 2: (u1, e1, 12:01)

The (u1, e1) record at index 2 replaces the one at index 0 because 12:01 is earlier than 12:05. The winning source indices are therefore [1, 2]. The returned order is the u2/e2 record from index 1, followed by the u1/e1 record from index 2.

2. Choose the hash map state

I use a dictionary named best_by_key. Its key is (user_id, event_id). Its value is (event, parsed_timestamp, source_index).

The invariant is that after processing each input event, every entry in best_by_key represents the earliest timestamp seen so far for that key. If two timestamps are equal, the earlier source occurrence stays because the replacement condition uses a strict < comparison.

3. Process each event in source order

Start with best_by_key = {}.

At index 0, (u1, e1) is absent, so store the event with timestamp 12:05 and index 0.

At index 1, (u2, e2) is absent, so store the event with timestamp 12:02 and index 1.

At index 2, (u1, e1) is already present. Compare 12:01 with the stored 12:05. Because 12:01 is earlier, replace the old value with the event from index 2.

After the scan:

  • (u1, e1) -> (12:01, index 2)
  • (u2, e2) -> (12:02, index 1)
4. Restore the required output order

The map tells us which record wins for each key, but the required output order is based on the source positions of those winning records. We therefore sort the retained map values by their stored source index.

The retained source indices become [1, 2]. We return the event from index 1 first and the event from index 2 second.

5. Explain why the result is correct

For every key, the stored record changes only when a strictly earlier timestamp is found. Therefore, after the scan finishes, each key is mapped to its earliest record. Sorting these winners by their retained source indices gives exactly the relative source-position order required by the question.

6. Explain complexity and edge cases

Let n be the number of input events and k be the number of distinct (user_id, event_id) keys. The scan takes O(n) expected time because Python dictionary lookup and update are O(1) on average. Sorting the k retained winners costs O(k log k). The total expected time is therefore O(n + k log k). The hash map uses O(k) auxiliary space.

For an empty input, the result is []. For equal timestamps on the same key, the strict < comparison keeps the first source occurrence.

Key Insight / Why This Solution Works

The key insight is to separate winner selection from final ordering. First, use a hash map to select the earliest record for each (user_id, event_id) key. Each map value stores the original event, its parsed timestamp, and its source index. The invariant is that after each processed event, every map entry is the earliest record seen so far for that key. After the scan, sort the winners by their stored source indices. This produces the required relative order of retained source positions without changing which record wins for each key.

Code
from datetime import datetime


def dedupeEventsKeepEarliest(events):
    # Store the current winning event, parsed timestamp, and source index
    # for each (user_id, event_id) key.
    best_by_key = {}

    # Traverse the input in source order so every saved index refers to
    # the exact original position of its event.
    for index, event in enumerate(events):
        key = (event["user_id"], event["event_id"])

        # Parse the ISO-8601 timestamp before comparing event times.
        timestamp = datetime.fromisoformat(event["ts"])

        # Keep a new key immediately. For an existing key, replace its
        # winner only when this timestamp is strictly earlier.
        # Equal timestamps therefore keep the first source occurrence.
        if key not in best_by_key or timestamp < best_by_key[key][1]:
            best_by_key[key] = (event, timestamp, index)

    # Sort the winning records by their retained source indices so the
    # final result follows the required relative source-position order.
    retained = [info[0] for info in sorted(best_by_key.values(), key=lambda info: info[2])]

    # Return the original winning event dictionaries in retained order.
    return retained


def main():
    # Run the exact example used in the approved diagram.
    events = [
        {"user_id": "u1", "event_id": "e1", "ts": "2026-01-01T12:05:00"},
        {"user_id": "u2", "event_id": "e2", "ts": "2026-01-01T12:02:00"},
        {"user_id": "u1", "event_id": "e1", "ts": "2026-01-01T12:01:00"},
    ]

    # The winning source indices are 1 and 2, so u2/e2 is returned
    # before the later source record u1/e1 with the earlier timestamp.
    result = dedupeEventsKeepEarliest(events)
    print(result)


if __name__ == "__main__":
    main()
Time & Space Complexity

Let n be the total number of input events and k be the number of distinct (user_id, event_id) keys. The first scan takes O(n) expected time because Python dictionary lookup and update are O(1) on average. We then sort the k retained winners by source index, which costs O(k log k). The total expected time is O(n + k log k). The hash map uses O(k) auxiliary space.

Where it is used

This pattern is useful in clickstream pipelines, event cleanup, log processing, and batch data preparation when duplicate business keys exist and one canonical record must be selected. It fits cases where the winning record is chosen by timestamp but the final output must still follow the original positions of the retained records.

Why Interviewers Ask This

This question checks whether you can deduplicate records without losing an ordering requirement. The interviewer is testing compound-key design, timestamp comparison, duplicate handling, and preservation of original source positions. It also checks whether you understand that selecting the earliest timestamp and ordering the retained records are separate steps. A strong answer maintains a clear map invariant, handles a later earlier-timestamp record correctly, and includes the final sorting cost in the complexity.

Common interview mistakes

A common mistake is keeping the first duplicate even when a later record has an earlier timestamp. Another mistake is sorting the final records by timestamp instead of by the retained source index. Returning best_by_key.values() directly is also wrong because dictionary key insertion order does not necessarily match the source positions of the final winning records after replacements. Candidates may also forget to save the winning source index. Using <= instead of < changes the equal-timestamp behavior by replacing the first occurrence. Finally, claiming O(n) total time ignores the O(k log k) final sort.

Interview tip

Explain the solution as two separate jobs: the hash map decides which record wins for each key, and the stored source index decides the final order. Then walk through index 2 in the example because that is the important case where a later source record has an earlier timestamp and replaces the previous winner.

Interviewer may ask next
How would the solution change if the events arrived as a stream and the final order still had to follow retained source positions?

The same hash-map winner-selection logic can process each event as it arrives. However, a record cannot be emitted safely as final output while later events may still contain an earlier timestamp for the same key. The current winner and its source index must remain buffered until the stream or processing window is complete. Then the k winners can be sorted by source index and emitted. The expected time is O(n + k log k), auxiliary space is O(k), and the main tradeoff is that final output must wait until the winners are known.

Can we avoid the final sort while keeping the exact solution shown in the diagram?

No. In the shown implementation, the hash map selects the correct winner for each key, but its value order is not the required order of the winners' retained source indices. Replacing the value for an existing dictionary key does not move that key to a new position. Therefore, this exact design needs the final sort by stored source index, which costs O(k log k). Avoiding that sort would require changing the implementation approach, such as adding another ordering step or performing another pass over the source data.

Disclaimer: This interview guide is for educational and informational purposes only. It is designed to help readers prepare, but it does not guarantee any interview result, hiring decision, offer, or outcome. Interview questions, hiring criteria, and preferred answers can vary by employer, interviewer, industry, location, and time. The examples and explanations reflect the authors' research and judgment, are provided without warranties of any kind, and should not be treated as the only correct approach. Diagrams are simplified illustrations intended to highlight the main components and their interactions; actual systems and implementations may be more complex. Alternative approaches may be equally valid or better suited to a particular question, context, or interviewer. To the fullest extent permitted by applicable law, the author, contributors, and publisher are not liable for decisions made, actions taken, or losses incurred based on this guide.

Company Notice: This guide is an independent educational resource and is not affiliated with, endorsed by, sponsored by, or approved by the company named in this guide. Company names are used only to identify interview experiences commonly reported by candidates. Interview practices can change without notice, and inclusion of company-specific content does not mean these questions are official, complete, or guaranteed to be asked. To the fullest extent permitted by law, the author, contributors, and publisher are not responsible for outcomes related to use of this material.

Content Accuracy and Verification: To the fullest extent permitted by applicable law, we do not represent or warrant that interview guides, questions, answers, examples, or diagrams are accurate, complete, current, error-free, or suitable for any particular purpose. You are responsible for independently reviewing and verifying the information before relying on it.