Skip to content

Commit f8a2a95

Browse files
committed
URL input fixes
Added for new alorithms, fixed string vs integer bug for Euclidean graphs, updated npm/Node.js versions
1 parent acf34a0 commit f8a2a95

File tree

6 files changed

+161
-42
lines changed

6 files changed

+161
-42
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Algorithms in Action
22

3-
![license](https://img.shields.io/badge/license-MIT-Green) ![node](https://img.shields.io/badge/node-v18.17.1-blue) ![npm](https://img.shields.io/badge/npm-v9.6.7-blue)
3+
![license](https://img.shields.io/badge/license-MIT-Green) ![node](https://img.shields.io/badge/node-v22.13.0-blue) ![npm](https://img.shields.io/badge/npm-10.9.2-blue)
44

55
Welcome to Algorithms In Action!
66

src/algorithms/parameters/HashingCHParam.js

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import PropTypes from 'prop-types';
2+
import { withAlgorithmParams } from './helpers/urlHelpers'
3+
4+
import { URLContext } from '../../context/urlState.js';
5+
16
import React, { useState, useContext, useEffect } from 'react';
27
import { GlobalContext } from '../../context/GlobalState';
38
import { GlobalActions } from '../../context/actions';
@@ -17,8 +22,8 @@ import {
1722
} from './helpers/ParamHelper';
1823
import { SMALL_SIZE, LARGE_SIZE } from '../controllers/HashingCommon';
1924

20-
// Algotiyhm information and magic phrases
21-
const ALGORITHM_NAME = 'Hashing (linear probing)';
25+
// Algorithm information and magic phrases
26+
const ALGORITHM_NAME = 'Hashing (chaining)';
2227
const HASHING_INSERT = 'Hashing Insertion';
2328
const HASHING_SEARCH = 'Hashing Search';
2429
const HASHING_EXAMPLE = 'PLACE HOLDER ERROR MESSAGE';
@@ -32,6 +37,8 @@ const UNCHECKED = {
3237
largeTable: false
3338
};
3439

40+
const DEFAULT_EXPAND = false;
41+
3542
// Styling of radio buttons
3643
const BlueRadio = withStyles({
3744
root: {
@@ -54,15 +61,23 @@ const ERROR_INVALID_RANGES = 'If you had entered ranges, please input valid rang
5461
* Chaining input component
5562
* @returns the component
5663
*/
57-
function HashingCHParam() {
64+
function HashingCHParam({ mode, list, value }) {
5865
const [message, setMessage] = useState(null);
5966
const { algorithm, dispatch } = useContext(GlobalContext);
60-
const [array, setArray] = useState(DEFAULT_ARRAY);
61-
const [search, setSearch] = useState(DEFAULT_SEARCH);
67+
const [array, setLocalArray] = useState(list || DEFAULT_ARRAY);
68+
const [search, setLocalSearch] = useState(DEFAULT_SEARCH);
6269
const [HASHSize, setHashSize] = useState({
6370
smallTable: true,
6471
largeTable: false,
6572
});
73+
const [expand, setExpand] = useState(DEFAULT_EXPAND);
74+
const { setNodes, setSearchValue } = useContext(URLContext);
75+
76+
useEffect(() => {
77+
setNodes(array);
78+
setSearchValue(search);
79+
}, [array, search])
80+
6681

6782
/**
6883
* Handle changes to input
@@ -72,6 +87,14 @@ function HashingCHParam() {
7287
setHashSize({ ...UNCHECKED, [e.target.name]: true })
7388
}
7489

90+
/**
91+
* Handle expand
92+
* @param {*} e the input box component
93+
*/
94+
const handleExpand = (e) => {
95+
setExpand(!expand)
96+
}
97+
7598
/**
7699
* Handle insert box inputs
77100
* @param {*} e the insert box component
@@ -94,7 +117,7 @@ function HashingCHParam() {
94117
mode: 'insertion',
95118
hashSize: hashSize,
96119
values,
97-
expand: false
120+
expand: expand
98121
});
99122
setMessage(successParamMsg(ALGORITHM_NAME));
100123
}
@@ -141,6 +164,14 @@ function HashingCHParam() {
141164
[HASHSize],
142165
);
143166

167+
// Use effect to detect changes in expand radio box choice
168+
useEffect(
169+
() => {
170+
document.getElementById('startBtnGrp').click();
171+
},
172+
[expand],
173+
);
174+
144175

145176
return (
146177
<>
@@ -151,7 +182,7 @@ function HashingCHParam() {
151182
mode="insertion"
152183
formClassName="formLeft"
153184
DEFAULT_VAL = {array}
154-
SET_VAL = {setArray}
185+
SET_VAL = {setLocalArray}
155186
REFRESH_FUNCTION={
156187
(() => {
157188
if (HASHSize.smallTable) {
@@ -174,8 +205,8 @@ function HashingCHParam() {
174205
buttonName="SEARCH"
175206
mode="search"
176207
formClassName="formRight"
177-
DEFAULT_VAL = {DEFAULT_SEARCH}
178-
SET_VAL = {setSearch}
208+
DEFAULT_VAL = {value || DEFAULT_SEARCH}
209+
SET_VAL = {setLocalSearch}
179210
ALGORITHM_NAME = {HASHING_SEARCH}
180211
handleSubmit={handleSearch}
181212
setMessage={setMessage}
@@ -212,6 +243,22 @@ function HashingCHParam() {
212243
className="checkbox"
213244
/>
214245
</div>
246+
247+
248+
<div>
249+
{HASHSize.smallTable && (
250+
<FormControlLabel
251+
control={
252+
<BlueRadio
253+
checked={expand}
254+
onClick={handleExpand}
255+
/>
256+
}
257+
label="Expand"
258+
className="checkbox"
259+
/>
260+
)}
261+
</div>
215262
</div>
216263

217264
{/* render success/error message */}
@@ -220,4 +267,11 @@ function HashingCHParam() {
220267
);
221268
}
222269

223-
export default HashingCHParam;
270+
// Define the prop types for URL Params
271+
HashingCHParam.propTypes = {
272+
alg: PropTypes.string.isRequired, // keep alg for all algorithms
273+
mode: PropTypes.string.isRequired, //keep mode for all algorithms
274+
list: PropTypes.string.isRequired,
275+
value: PropTypes.string.isRequired
276+
};
277+
export default withAlgorithmParams(HashingCHParam);
Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,39 @@
11
/* eslint-disable no-prototype-builtins */
22
/* eslint-disable jsx-a11y/label-has-associated-control */
3-
import React, { useState } from 'react';
3+
import React, { useState, useContext, useEffect } from 'react';
4+
import { URLContext } from '../../context/urlState';
45
import { genRandNumList } from './helpers/ParamHelper';
56
import ListParam from './helpers/ListParam';
7+
import PropTypes from 'prop-types';
8+
import { withAlgorithmParams } from './helpers/urlHelpers';
69
import '../../styles/Param.scss';
710

811
const DEFAULT_NODES = genRandNumList(10, 1, 100);
9-
const STRAIGHT_RADIX_SORT = 'Straight Radix Sort';
10-
const STRAIGHT_RADIX_SORT_EXAMPLE = 'Please follow the example provided: 0,1,2,3,4';
12+
const DEFAULT_MODE = 'sort';
13+
const Straight_RADIX_SORT = 'Straight Radix Sort';
14+
const MSDRS_SORT_EXAMPLE = 'Please follow the example provided: 0,1,2,3,4';
1115

12-
function StraightRadixSortParam() {
16+
function StraightRadixSortParam({ mode, list }) {
1317
const [message, setMessage] = useState(null);
14-
const [nodes, setNodes] = useState(DEFAULT_NODES);
18+
const [localNodes, setLocalNodes] = useState(list || DEFAULT_NODES);
19+
const { setNodes } = useContext(URLContext);
20+
21+
useEffect(() => {
22+
setNodes(localNodes);
23+
}, [localNodes]);
1524

1625
return (
1726
<>
1827
<div className="form">
1928
<ListParam
2029
name="radixSortStraight"
2130
buttonName="Sort"
22-
mode="sort"
31+
mode={mode || DEFAULT_MODE}
2332
formClassName="formLeft"
24-
DEFAULT_VAL={nodes}
25-
SET_VAL={setNodes}
26-
ALGORITHM_NAME={STRAIGHT_RADIX_SORT}
27-
EXAMPLE={STRAIGHT_RADIX_SORT_EXAMPLE}
33+
DEFAULT_VAL={localNodes}
34+
SET_VAL={setLocalNodes}
35+
ALGORITHM_NAME={Straight_RADIX_SORT}
36+
EXAMPLE={MSDRS_SORT_EXAMPLE}
2837
setMessage={setMessage}
2938
/>
3039
</div>
@@ -35,4 +44,10 @@ function StraightRadixSortParam() {
3544
);
3645
}
3746

38-
export default StraightRadixSortParam;
47+
StraightRadixSortParam.propTypes = {
48+
alg: PropTypes.string.isRequired,
49+
mode: PropTypes.string.isRequired,
50+
list: PropTypes.string.isRequired,
51+
}
52+
53+
export default withAlgorithmParams(StraightRadixSortParam);

src/algorithms/parameters/helpers/EuclideanMatrixParams.js

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -270,10 +270,18 @@ function EuclideanMatrixParams({
270270
// and third is weight (if third is missing, weight defaults to 1)
271271
const [coordsTxt, setCoordsTxt] = useState(graphEgs[0].coords);
272272
const [edgesTxt, setEdgesTxt] = useState(graphEgs[0].edges);
273-
const [startNode, setStartNode] = useState(defaultStart);
273+
// Note that with URL input, defaultStart is a string rather than an
274+
// integer, so we convert with unary + (otherwise when we compute
275+
// start+1 it concatenates strings...)
276+
const [startNode, setStartNode] = useState(+defaultStart);
274277
// XXX not sure if endNodesTxt needs to be in State
275278
const [endNodesTxt, setEndNodesTxt] = useState(nums2Txt(defaultEnd));
276-
const [endNodes, setEndNodes] = useState(Array.isArray(defaultEnd) ? defaultEnd : [defaultEnd]);
279+
// A* can have multiple end nodes in general so we use an array.
280+
// However, other code assumes a single integer end node so the code
281+
// here supports both and multiple end nodes are not yet supported in
282+
// A* (XXX might fix this one day) Comment on defaultStart re strings
283+
// versus integers also applies
284+
const [endNodes, setEndNodes] = useState(Array.isArray(defaultEnd) ? defaultEnd : [+defaultEnd]);
277285
// const [graphChoice, setgraphChoice] = useState(GRAPHCHOICERAND);
278286
const [graphChoice, setgraphChoice] = useState(1);
279287

@@ -398,11 +406,16 @@ function EuclideanMatrixParams({
398406
// required
399407
// useEffect() triggers handleSearch() to update animation
400408
const updateEndNode = (newEnd) => {
401-
newEnd = (newEnd + size + 1) % (size + 1);
402-
if (newEnd === 0 && ALGORITHM_NAME === 'A* Algorithm')
403-
newEnd = size;
409+
// use modulo size + 1 to wrap around
410+
let newEnd1 = (newEnd + size + 1) % (size + 1);
411+
// avoid 0 for A*, which has compulsory end node(s)
412+
if (newEnd1 === 0 && ALGORITHM_NAME === 'A* Algorithm')
413+
if (newEnd === size+1) // we incremented end
414+
newEnd1 = 1;
415+
else
416+
newEnd1 = size; // we decremented end
404417
setMessage(null);
405-
setEndNodes([newEnd]);
418+
setEndNodes([newEnd1]);
406419
// handleSearch();
407420
};
408421

@@ -736,18 +749,20 @@ function EuclideanMatrixParams({
736749
// setMessage={setMessage}
737750
// />
738751
// </div>);
739-
// XXX would be nice to display ' ' instead of '0'
740752
endButton =
741753
(<div className="sLineButtonContainer">
742754
<button className="endBtn" onClick={() => updateEndNode(endNodes[0] - 1)}>
743755
744756
</button>
745-
<span className='size'>End: {endNodes[0]}</span>
757+
<span className='size'>End:
758+
{endNodes[ 0]=== 0 ? ' _' : endNodes[0]}
759+
</span>
746760
<button className="sizeBtn" onClick={() => updateEndNode(endNodes[0] + 1)}>
747761
+
748762
</button>
749763
</div>);
750764

765+
/* // not used
751766
let startButton = '';
752767
if (defaultStart !== null)
753768
startButton =
@@ -761,6 +776,7 @@ function EuclideanMatrixParams({
761776
</button>
762777
763778
</div>);
779+
*/
764780

765781
let weightButton = '';
766782
if (!unweighted)
@@ -952,15 +968,18 @@ const graphEgsNames = (graphEgs) => {
952968
}
953969

954970
// converts [1,2,3] into '1,2,3' etc
971+
// Used for end nodes, but a bunch of things only support a single end
972+
// node so we also support single numbers here
955973
const nums2Txt = (nums) => {
956974
if (nums === null) return '';
957975
let txt = ``;
958976
if (!Array.isArray(nums)) {
959-
console.log('Expected an array but received:', nums);
977+
console.log('Hoped for an array but received:', nums);
960978
if (typeof nums === 'number') {
961979
txt = nums.toString();
962980
}
963-
return txt; // or handle this case appropriately
981+
console.log('Sometimes in life you just have to make do with what you are given');
982+
return txt; // XXX or handle this case appropriately
964983
}
965984
nums.forEach((n) => {
966985
txt += n + `,`;

src/algorithms/parameters/msort_arr_bup.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
// Adapted from Quicksort - could rename a few things
22

33
/* eslint-disable no-unused-vars */
4-
import React, { useState, useEffect } from 'react';
4+
import React, { useState, useEffect, useContext } from 'react';
55
import FormControlLabel from '@mui/material/FormControlLabel';
66
import Radio from '@mui/material/Radio';
77
import { withStyles } from '@mui/styles';
88
import { genRandNumList, quicksortPerfectPivotArray } from './helpers/ParamHelper';
99
import ListParam from './helpers/ListParam';
1010
import '../../styles/Param.scss';
1111

12+
import PropTypes from 'prop-types'; // Import this for URL Param
13+
import { withAlgorithmParams } from './helpers/urlHelpers' // Import this for URL Param
14+
15+
import { URLContext } from '../../context/urlState';
16+
1217
const DEFAULT_ARRAY_GENERATOR = genRandNumList.bind(null, 12, 1, 50);
1318
const DEFAULT_ARR = DEFAULT_ARRAY_GENERATOR();
1419
const MERGE_SORT = 'Merge Sort (bottom up)';
@@ -31,17 +36,20 @@ const BlueRadio = withStyles({
3136
// eslint-disable-next-line react/jsx-props-no-spreading
3237
})((props) => <Radio {...props} />)
3338

34-
function MergesortParam() {
39+
function MergesortParam({ list }) {
3540
const [message, setMessage] = useState(null)
36-
const [array, setArray] = useState(DEFAULT_ARR)
41+
const [array, setArray] = useState(list || DEFAULT_ARR)
42+
const { setNodes } = useContext(URLContext)
3743
const [QSCase, setQSCase] = useState({
3844
random: true,
3945
sortedAsc: false,
4046
// bestCase: false,
4147
sortedDesc: false
4248
});
4349

44-
50+
useEffect(() => {
51+
setNodes(array);
52+
}, [array]);
4553

4654
// XXX best case definitely not needed; could skip choice of cases
4755
// function for choosing the type of input
@@ -155,4 +163,11 @@ function MergesortParam() {
155163
)
156164
}
157165

158-
export default MergesortParam
166+
// Define the prop types for URL Params
167+
MergesortParam.propTypes = {
168+
alg: PropTypes.string.isRequired,
169+
mode: PropTypes.string.isRequired,
170+
list: PropTypes.string.isRequired
171+
};
172+
173+
export default withAlgorithmParams(MergesortParam); // Export with the wrapper for URL Params

0 commit comments

Comments
 (0)