Skip to main content

Transfer List

Difficulty: Advanced

Problem Statement​

Two lists with buttons to move selected items left/right using checkbox selection.

Live Demo​

Try the running app below β€” this is the target behavior for the challenge.

Transfer List β€” Live Demo
List A
  • HTML
  • CSS
  • JavaScript
List B
  • TypeScript

What Interviewers Expect​

  • Independent list state.
  • Selection tracked via Set/checkbox.
  • Disable transfer buttons when nothing selected.

Step-by-Step Implementation​

Step 1 β€” Two list states​

Maintain left and right arrays separately.

const [left, setLeft] = useState(['HTML', 'CSS']);
const [right, setRight] = useState(['TypeScript']);

Step 2 β€” Selection Set​

Track selected item names per list.

const [selLeft, setSelLeft] = useState(new Set());

Step 3 β€” Move selected items​

Filter moved items out of source and append to destination.

const moving = from.filter((x) => selected.has(x));

Complete Implementation​

Copy-paste ready single-file solution. Use this as your reference after attempting the challenge yourself, or paste it into CodeSandbox / StackBlitz to run locally.

App.jsx
import React, {useState} from 'react';

function TransferPanel({items, selected, setSelected, label}) {
const toggle = (name) => {
setSelected((prev) => {
const next = new Set(prev);
if (next.has(name)) next.delete(name);
else next.add(name);
return next;
});
};

return (
<div
style={{
flex: 1,
minWidth: 220,
border: '1px solid #ccc',
borderRadius: 8,
padding: '0.75rem',
}}
>
<strong>{label}</strong>
<ul style={{listStyle: 'none', padding: 0, marginTop: '0.5rem'}}>
{items.map((name) => (
<li key={name} style={{display: 'flex', gap: '0.5rem', marginBottom: '0.35rem'}}>
<input
type="checkbox"
checked={selected.has(name)}
onChange={() => toggle(name)}
/>
{name}
</li>
))}
</ul>
</div>
);
}

export default function App() {
const [left, setLeft] = useState(['HTML', 'CSS', 'JavaScript']);
const [right, setRight] = useState(['TypeScript']);
const [selLeft, setSelLeft] = useState(new Set());
const [selRight, setSelRight] = useState(new Set());

const move = (from, setFrom, to, setTo, selected, clearSelected) => {
const moving = from.filter((item) => selected.has(item));
if (!moving.length) return;

setFrom(from.filter((item) => !selected.has(item)));
setTo([...to, ...moving]);
clearSelected(new Set());
};

return (
<div style={{display: 'flex', gap: '0.75rem', alignItems: 'flex-start'}}>
<TransferPanel
items={left}
selected={selLeft}
setSelected={setSelLeft}
label="List A"
/>

<div style={{display: 'grid', gap: '0.5rem', alignSelf: 'center'}}>
<button
type="button"
disabled={!selLeft.size}
onClick={() => move(left, setLeft, right, setRight, selLeft, setSelLeft)}
>
β†’
</button>
<button
type="button"
disabled={!selRight.size}
onClick={() => move(right, setRight, left, setLeft, selRight, setSelRight)}
>
←
</button>
</div>

<TransferPanel
items={right}
selected={selRight}
setSelected={setSelRight}
label="List B"
/>
</div>
);
}

Final Checklist​

  • UI works for primary flow
  • Edge cases handled (empty/disabled/loading where relevant)
  • State updates are immutable
  • Components are readable and reasonably split
  • You can explain trade-offs out loud in an interview

Next Challenge​

Continue to the next item in the sidebar when you're comfortable implementing this from scratch in 30–45 minutes.