|
1 | | -import { useState } from 'react'; |
| 1 | +import { useMemo, useState } from 'react'; |
2 | 2 |
|
3 | 3 | export default function ArrayVisualizer() { |
4 | 4 | const [arr, setArr] = useState<string[]>([]); |
5 | 5 | const [val, setVal] = useState(''); |
6 | 6 | const [idx, setIdx] = useState('0'); |
| 7 | + const [cap, setCap] = useState<number>(4); |
| 8 | + const [capInput, setCapInput] = useState('4'); |
| 9 | + const [error, setError] = useState<string | null>(null); |
| 10 | + |
| 11 | + const n = arr.length; |
| 12 | + const iNum = useMemo(() => Number(idx), [idx]); |
| 13 | + const idxIsInt = Number.isInteger(iNum) && iNum >= 0; |
| 14 | + const idxValidForSet = idxIsInt && iNum < cap; // allow setting within capacity |
| 15 | + const idxValidForInsert = idxIsInt && iNum <= n && n < cap; // insert shifts, must have space |
| 16 | + |
| 17 | + const clearErrorSoon = () => setTimeout(() => setError(null), 1500); |
| 18 | + |
| 19 | + const applySize = () => { |
| 20 | + const next = Number(capInput); |
| 21 | + if (!Number.isInteger(next) || next < 0 || next > 50) { |
| 22 | + setError('Size must be an integer 0..50'); |
| 23 | + clearErrorSoon(); |
| 24 | + return; |
| 25 | + } |
| 26 | + setCap(next); |
| 27 | + if (next < arr.length) setArr(a => a.slice(0, next)); |
| 28 | + }; |
7 | 29 |
|
8 | 30 | const push = () => { |
9 | | - if (!val) return; setArr(a => [...a, val]); setVal(''); |
| 31 | + if (!val) return; |
| 32 | + if (n >= cap) { setError('Array is full'); clearErrorSoon(); return; } |
| 33 | + setArr(a => [...a, val]); setVal(''); |
10 | 34 | }; |
11 | 35 | const insertAt = () => { |
12 | | - const i = Number(idx); if (!Number.isInteger(i) || i < 0 || i > arr.length) return; |
13 | | - const a = arr.slice(); a.splice(i, 0, val || ''); setArr(a); setVal(''); |
| 36 | + if (!idxIsInt) { setError('Index must be an integer'); clearErrorSoon(); return; } |
| 37 | + if (n >= cap) { setError('Array is full'); clearErrorSoon(); return; } |
| 38 | + if (iNum < 0 || iNum > n) { setError(`Insert index must be 0..${n}`); clearErrorSoon(); return; } |
| 39 | + const a = arr.slice(); a.splice(iNum, 0, val || ''); setArr(a); setVal(''); |
14 | 40 | }; |
15 | 41 | const removeAt = () => { |
16 | | - const i = Number(idx); if (!Number.isInteger(i) || i < 0 || i >= arr.length) return; |
17 | | - const a = arr.slice(); a.splice(i, 1); setArr(a); |
| 42 | + if (!idxIsInt || iNum < 0 || iNum >= n) { setError(`Remove index must be 0..${Math.max(0, n - 1)}`); clearErrorSoon(); return; } |
| 43 | + const a = arr.slice(); a.splice(iNum, 1); setArr(a); |
18 | 44 | }; |
19 | 45 | const setAt = () => { |
20 | | - const i = Number(idx); if (!Number.isInteger(i) || i < 0 || i >= arr.length) return; |
21 | | - const a = arr.slice(); a[i] = val; setArr(a); setVal(''); |
| 46 | + if (!idxIsInt || iNum < 0 || iNum >= cap) { setError(`Set index must be 0..${Math.max(0, cap - 1)}`); clearErrorSoon(); return; } |
| 47 | + // Setting beyond current length expands up to cap by filling empty slots |
| 48 | + const a = arr.slice(); |
| 49 | + while (a.length < iNum) a.push(''); |
| 50 | + if (iNum === a.length && a.length >= cap) { setError('Array is full'); clearErrorSoon(); return; } |
| 51 | + a[iNum] = val; |
| 52 | + if (iNum === arr.length) { |
| 53 | + // grew by one |
| 54 | + if (a.length > cap) { setError('Array is full'); clearErrorSoon(); return; } |
| 55 | + } |
| 56 | + setArr(a); |
| 57 | + setVal(''); |
22 | 58 | }; |
23 | 59 |
|
24 | 60 | return ( |
25 | 61 | <div className="space-y-3"> |
26 | 62 | <div className="flex flex-wrap gap-2 items-center"> |
27 | 63 | <input className="border rounded px-2 py-1 w-32 bg-white dark:bg-gray-900" placeholder="value" value={val} onChange={e => setVal(e.target.value)} /> |
28 | | - <input className="border rounded px-2 py-1 w-20 bg-white dark:bg-gray-900" placeholder="index" value={idx} onChange={e => setIdx(e.target.value)} /> |
| 64 | + <input className="border rounded px-2 py-1 w-24 bg-white dark:bg-gray-900" placeholder={`index`} value={idx} onChange={e => setIdx(e.target.value)} /> |
29 | 65 | <button className="px-3 py-1 rounded bg-blue-600 text-white" onClick={push}>Push</button> |
30 | 66 | <button className="px-3 py-1 rounded bg-emerald-600 text-white" onClick={insertAt}>Insert@i</button> |
31 | | - <button className="px-3 py-1 rounded bg-amber-600 text-white" onClick={setAt}>Set@i</button> |
32 | | - <button className="px-3 py-1 rounded bg-red-600 text-white" onClick={removeAt} disabled={!arr.length}>Remove@i</button> |
| 67 | + <button className="px-3 py-1 rounded bg-amber-600 text-white" onClick={setAt} disabled={!idxValidForSet}>Set@i</button> |
| 68 | + <button className="px-3 py-1 rounded bg-red-600 text-white" onClick={removeAt} disabled={n === 0}>Remove@i</button> |
33 | 69 | <button className="px-3 py-1 rounded bg-gray-600 text-white" onClick={() => setArr([])} disabled={!arr.length}>Clear</button> |
| 70 | + <span className="ml-4 text-xs text-gray-500">Size</span> |
| 71 | + <input className="border rounded px-2 py-1 w-20 bg-white dark:bg-gray-900" value={capInput} onChange={e => setCapInput(e.target.value)} /> |
| 72 | + <button className="px-3 py-1 rounded border border-gray-300 dark:border-gray-700" onClick={applySize}>Apply</button> |
| 73 | + {error && <span className="text-xs text-red-600">{error}</span>} |
34 | 74 | </div> |
35 | 75 | <div className="overflow-auto"> |
36 | 76 | <div className="flex items-stretch"> |
37 | | - {arr.map((v, i) => ( |
38 | | - <div key={i} className="m-1"> |
| 77 | + {Array.from({ length: cap }).map((_, i) => ( |
| 78 | + <div key={i} className="m-1 text-center"> |
39 | 79 | <div className="text-center text-xs text-gray-500">{i}</div> |
40 | | - <div className="w-20 h-12 border rounded flex items-center justify-center bg-white dark:bg-gray-900"> |
41 | | - {v} |
| 80 | + <div className={`w-20 h-12 border rounded flex items-center justify-center bg-white dark:bg-gray-900 ${i === iNum && idxValidForSet ? 'ring-2 ring-blue-500' : ''}`}> |
| 81 | + {arr[i] ?? ''} |
42 | 82 | </div> |
43 | 83 | </div> |
44 | 84 | ))} |
45 | | - {arr.length === 0 && <div className="text-sm text-gray-500">(empty array)</div>} |
| 85 | + {cap === 0 && <div className="text-sm text-gray-500">(size = 0)</div>} |
46 | 86 | </div> |
47 | 87 | </div> |
| 88 | + <div className="text-xs text-gray-500">length = {n} • capacity = {cap}</div> |
| 89 | + <div className="text-xs text-gray-500">Insert index range: 0..{n} • Set index range: 0..{Math.max(0, cap - 1)}</div> |
48 | 90 | </div> |
49 | 91 | ); |
50 | 92 | } |
51 | | - |
|
0 commit comments