this is my app.js The app.js file render the Leaderboard component for each route like /rank,/point,/age,/name
<Router>
<Switch>
<Route path="/">
<div className="App">
<h8k-navbar header={title}></h8k-navbar>
<LeaderBoard />
</div>
</Route>
<Route path="/name">
<div className="App">
<h8k-navbar header={title}></h8k-navbar>
<LeaderBoard />
</div>
</Route>
<Route path="/rank">
<div className="App">
<h8k-navbar header={title}></h8k-navbar>
<LeaderBoard sorted = {lists}/>
</div>
</Route>
<Route path="/age">
<div className="App">
<h8k-navbar header={title}></h8k-navbar>
<LeaderBoard />
</div>
</Route>
<Route path="/points">
<div className="App">
<h8k-navbar header={title}></h8k-navbar>
<LeaderBoard />
</div>
</Route>
</Switch>
</Router>
this is my json: here is my data contain rank,point,name,age. I want this detail should be sorted by rank,point,name,age .that is when the route is /rank The table data in Leaderboard should be sorted by rank and render that
{"list":[
{
"rank":"1",
"points":"1025",
"name":"John Doe",
"age":"27"
},
{
"rank":"3",
"points":"245",
"name":"Elizabeth",
"age":"17"
},
{
"rank":"2",
"points":"566",
"name":"Samantha",
"age":"22"
}]
}
This is my leaderboard component
function LeaderBoard({sorted}) {
let history = useHistory();
let newList;
console.log(sorted);
let lists = response.list
const handleClick =()=>{
history.push('/rank')
}
return (
<div className="text-center mt-50">
<div>
<div>
<button data-testid="route-rank" className='outlined' type="button" onClick={handleClick}>Rank</button>
<button data-testid="route-name" className='outlined' type="button">Name</button>
<button data-testid="route-points" className='outlined' type="button">Points</button>
<button data-testid="route-age" className='outlined' type="button">Age</button>
</div>
</div>
<div className="card mx-auto pb-20 mb-30" style={{ width: '50%' }}>
<table className="mt-50" data-testid="app-table">
<thead>
<tr>
<th>Rank</th>
<th>Name</th>
<th className="numeric">Points</th>
<th className="numeric">Age</th>
</tr>
</thead>
<tbody data-testid="app-tbody">
{lists.map((ele,index)=>{
// console.log(index)
return(
<tr key={ele.rank}>
<td data-testid={`rank-${index}`}>{ele.rank}</td>
<td data-testid={`name-${index}`}>{ele.name}</td>
<td data-testid={`points-${index}`} className="numeric">{ele.points}</td>
<td data-testid={`age-${index}`} className="numeric">{ele.age}</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
);
}
export default LeaderBoard;
how to sort the json file by each rank,name,point,age and render it
CodePudding user response:
You should export the JSON array.
Import that file in Leaderboard component
import List from './path/to/json_file/list.json';
Then write the code to sort the array.
if you are lodash library then it's fairly simple
const orderedList= _.sortBy(List, list => list.rank
)
CodePudding user response:
You could just pass a property sortBy
(or obtain that information from the URL path) depending on which key you want to sort by and then sort your data within the component based on that key.
Here one way how you could do that:
const data = [
{
rank: "1",
points: "1025",
name: "John Doe",
age: "27",
},
{
rank: "3",
points: "245",
name: "Elizabeth",
age: "17",
},
{
rank: "2",
points: "566",
name: "Samantha",
age: "22",
},
];
const Table = ({ list, sortBy }) => {
const [data, setData] = React.useState(list);
const [sortedBy, setSortedBy] = React.useState(sortBy);
const sorted = data.sort((a, b) => {
let aVal = a[sortedBy];
let bVal = b[sortedBy];
if (typeof aVal === typeof bVal && aVal !== undefined) {
// parse points to Number so they are sorted correctly
if (sortedBy === "points") {
aVal = Number(aVal);
bVal = Number(bVal);
}
if (typeof aVal === "number") return aVal - bVal;
else if (typeof aVal === "string") return aVal.localeCompare(bVal);
}
return 1;
});
return (
<React.Fragment>
<h3>{`Sorted by '${sortBy}'`}</h3>
<table>
<thead>
<tr className="">
<th>Rank</th>
<th>Points</th>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
{sorted.map((item) => (
<tr key={item.rank}>
<td>{item.rank}</td>
<td>{item.points}</td>
<td>{item.name}</td>
<td>{item.age}</td>
</tr>
))}
</tbody>
</table>
</React.Fragment>
);
};
const Wrapper = () => {
return (
<React.Fragment>
<Table list={data} sortBy="rank" />
<Table list={data} sortBy="points" />
<Table list={data} sortBy="name" />
<Table list={data} sortBy="age" />
</React.Fragment>
);
};
ReactDOM.render(
<Wrapper/>,
document.getElementById("root")
);
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id="root"></div>
The sort()
function here only really compares all values that are strings or numbers and parsed points
to Number
before comparison otherwise a string comparison would be performed yielding unexpected results. You may need to adjust that function to your needs but in essence this example shows what you need to do.
CodePudding user response:
Here you have a demo of the below-proposed solution. Your question seems to be a little bit confusing because you're passing a prop called sorted and, at the same time you're using lists
inside the Leaderboard
component. So I wasn't sure if you wanted to sort the list outside of Leaderboard
or inside it (there's only one route where you actually pass the list through the Leaderboard
). So I assumed you just want ideas.
From inside your Leaderboard
component you can call useLocation()
to get access to the pathname and make the decision based on that. Like this:
const location = useLocation();
// Turns the pathname string into an array
const splittedPathName = location.pathname.split('/');
// Gets the last item in the array to be used as the sort criteria
const path = splittedPathName.pop() ?? '';
// path === 'age', 'rank', '', etc,
Then you can sort your list before returning your actual HTML:
// we don't want to mutate the original data during the sorting
const sortedList = structuredClone(lists);
if (path) {
// the sorting algorithm here will depend on the type of your
// data. I'm considering it's always string
sortedList.sort((a, b) => a[path].localeCompare(b[path]));
}
After that, you can use the sortedList
instead of your lists
in your returned HTML.
Performance improvement (if required)
If your list is big, sorting it can make your component to cause short freezing periods to the page, which is not a good user experience. You can work it around with the new useDeferredValue
hook:
const deferredValue = useDeferredValue(path)
const sortedList = useMemo(() => {
// we don't want to mutate the original data
const newList = structuredClone(lists);
if (!path) {
return newList;
}
// the sorting algorithm here will depend on the type of your
// data. I'm considering it's always string
newList.sort((a, b) => a[path].localeCompare(b[path]));
return newList;
}, [lists, path]);