admin管理员组文章数量:1404923
I have a React table that has sortable headers by desc and asc values.
It parses string values to numbers for sorting. However, my numeric(x)
function fails to deliver when it meets a null
value in my dataset.
Here is the error: TypeError: Cannot read property 'slice' of null
Below is my function, and I also added code on how I implement numeric
to my Comparator()
for sorting
function numeric(x) {
const val = parseFloat(x);
if (isNaN(val)) {
return parseFloat(x.slice(1));
} else {
return val;
}
}
function descendingComparator(a, b, orderBy)
{
const numericA = numeric(a[orderBy]);
const numericB = numeric(b[orderBy]);
if (numericB < numericA){
return -1
}
if (numericB > numericA){
return 1
}
return 0
}
How should I handle nulls in my numeric function? Realistically, they should be ignored and be placed at the bottom of the pecking order when sorting by asc and desc.
EDIT: example data types for input x:
- 10
- 10%
- $10
- .1
- abc (string characters)
If not careful, the Null values can act as a 0
when sorting data. It's important to note that Null simply means no data available, they should not be given a numerical value.
EDIT EDIT: Important information about my Comparator and sorting functions
function getComparator(order, orderBy)
{
return order === "desc"
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy)
}
const sortedRowInformation = (rowArray, parator) =>
{
const stabilizedRowArray = rowArray.map((el, index) => [el, index])
stabilizedRowArray.sort((a, b) =>
{
const order = parator(a[0], b[0])
if (order !== 0) return order
return a[1] - b[1]
})
return stabilizedRowArray.map((el) => el[0])
}
An example of my table that uses all of these functions pieced together:
export default function TableContent(props)
{
const [orderDirection, setOrderDirection] = useState('asc');
const [valueToOrderBy, setValueToOrderBy] = useState('symbol');
const { data } = props;
const handleRequestSort = (event, property) =>
{
const isAscending = (valueToOrderBy === property && orderDirection === 'asc')
setValueToOrderBy(property)
setOrderDirection(isAscending ? 'desc' : 'asc')
}
return (
<>
<TableContainer>
<Table>
<AdvancedStatsHeaders
data={data}
valueToOrderBy={valueToOrderBy}
orderDirection={orderDirection}
handleRequestSort={handleRequestSort}
/>
<TableBody>
{
sortedRowInformation(data, getComparator(orderDirection, valueToOrderBy))
.map((stock, index) => (
<TableRow key = {index} >
<TableCell>
{stock.symbol}
</TableCell>
<TableCell>
{stock.enterprisevalue}
</TableCell>
<TableCell>
{stock.enterprise_value_revenue}
</TableCell>
<TableCell>
{stock.revenuepershare}
</TableCell>
<TableCell>
{stock.debt_to_equity}
</TableCell>
<TableCell>
{stock.ebitda}
</TableCell>
<TableCell>
{stock.profitmargin}
</TableCell>
<TableCell>
{stock.price_to_sales}
</TableCell>
<TableCell>
{stock.price_to_book}
</TableCell>
<TableCell>
{stock.put_call_ratio}
</TableCell>
</TableRow>
))
}
</TableBody>
</Table>
</TableContainer>
</>
);
}
I have a React table that has sortable headers by desc and asc values.
It parses string values to numbers for sorting. However, my numeric(x)
function fails to deliver when it meets a null
value in my dataset.
Here is the error: TypeError: Cannot read property 'slice' of null
Below is my function, and I also added code on how I implement numeric
to my Comparator()
for sorting
function numeric(x) {
const val = parseFloat(x);
if (isNaN(val)) {
return parseFloat(x.slice(1));
} else {
return val;
}
}
function descendingComparator(a, b, orderBy)
{
const numericA = numeric(a[orderBy]);
const numericB = numeric(b[orderBy]);
if (numericB < numericA){
return -1
}
if (numericB > numericA){
return 1
}
return 0
}
How should I handle nulls in my numeric function? Realistically, they should be ignored and be placed at the bottom of the pecking order when sorting by asc and desc.
EDIT: example data types for input x:
- 10
- 10%
- $10
- .1
- abc (string characters)
If not careful, the Null values can act as a 0
when sorting data. It's important to note that Null simply means no data available, they should not be given a numerical value.
EDIT EDIT: Important information about my Comparator and sorting functions
function getComparator(order, orderBy)
{
return order === "desc"
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy)
}
const sortedRowInformation = (rowArray, parator) =>
{
const stabilizedRowArray = rowArray.map((el, index) => [el, index])
stabilizedRowArray.sort((a, b) =>
{
const order = parator(a[0], b[0])
if (order !== 0) return order
return a[1] - b[1]
})
return stabilizedRowArray.map((el) => el[0])
}
An example of my table that uses all of these functions pieced together:
export default function TableContent(props)
{
const [orderDirection, setOrderDirection] = useState('asc');
const [valueToOrderBy, setValueToOrderBy] = useState('symbol');
const { data } = props;
const handleRequestSort = (event, property) =>
{
const isAscending = (valueToOrderBy === property && orderDirection === 'asc')
setValueToOrderBy(property)
setOrderDirection(isAscending ? 'desc' : 'asc')
}
return (
<>
<TableContainer>
<Table>
<AdvancedStatsHeaders
data={data}
valueToOrderBy={valueToOrderBy}
orderDirection={orderDirection}
handleRequestSort={handleRequestSort}
/>
<TableBody>
{
sortedRowInformation(data, getComparator(orderDirection, valueToOrderBy))
.map((stock, index) => (
<TableRow key = {index} >
<TableCell>
{stock.symbol}
</TableCell>
<TableCell>
{stock.enterprisevalue}
</TableCell>
<TableCell>
{stock.enterprise_value_revenue}
</TableCell>
<TableCell>
{stock.revenuepershare}
</TableCell>
<TableCell>
{stock.debt_to_equity}
</TableCell>
<TableCell>
{stock.ebitda}
</TableCell>
<TableCell>
{stock.profitmargin}
</TableCell>
<TableCell>
{stock.price_to_sales}
</TableCell>
<TableCell>
{stock.price_to_book}
</TableCell>
<TableCell>
{stock.put_call_ratio}
</TableCell>
</TableRow>
))
}
</TableBody>
</Table>
</TableContainer>
</>
);
}
Share
Improve this question
edited Feb 28, 2021 at 19:58
yung peso
asked Feb 25, 2021 at 18:45
yung pesoyung peso
1,8068 gold badges41 silver badges88 bronze badges
2
-
I don't understand why you are doing that slice, but one way of avoiding this specific error is to do something like this:
if (x && isNaN(val))
. Or you can have a try-catch block. – Håken Lid Commented Feb 25, 2021 at 18:55 - @HåkenLid, I'm not sure what to return after that statement. – yung peso Commented Feb 25, 2021 at 19:04
3 Answers
Reset to default 6 +50The challenge is about sorting numbers OR text with the same function, based on a column name and a direction. The difficulty is about making sure we deal with a number or with text... to apply the right .sort()
callback.
Additionally, there are two things to take in account:
- Some strings may need to be "reformatted" as numbers
- Some values may be
null
and should always must ends up at the end of the sorting.
Okay! First, let's go through your functions from the first one:
sortedRowInformation(data, getComparator(orderDirection, valueToOrderBy)).map(...)
That sortedRowInformation
really looks like a .sort()
function nested in a .sort()
function... With two .map()
applied on a stabilizedRowArray
sub-array...
I have to admit I discarded it right away. I assumed the expected result by its pretty descriptive name and the 1st argument being the data
.
The second argument... Where getComparator()
is a function call with the two ponant state properties as argument. That returns two possible functions (lol, the same function with or without a minus sign in front of its evaluation...). So calling that descendingComparator
function again calls another function numeric()
where our null
problem is.
And all this process to just return -1
, 0
or 1
to be used as that second argument for sortedRowInformation()
... Which is the basic .sort()
internal job.
Just describing this process should raise a big flag. You nicely overplicated everything.
Solution
Here are my solution explanations starting from the other end:The numeric()
function is okay to isolate as a function. But that is where you had difficulties due to the null
values ing in 3 functions above... So what if we put the null
values aside from that logic and always assume a non-null value?
So now that we decided there is no null
values, we can test them more easily for number or string. Anyway there is a special case where a string can finally be a number when removing the $
and as (thousand separator).
I came up with this custom isNumeric()
function:
function isNumeric(x) {
let value = !isNaN(x) ? x: parseFloat(x.replace(/[\$,]/g, ""))
return {isNum:!isNaN(value), value}
}
That function returns an object containing a boolean and a "processed" value: a number or NaN
.
Now... Getting back to the starting point for the sorting, here is a function to sort the data
:
function sortAll(data, orderDirection, valueToOrderBy) {
// Filter the nulls in an array and the rest in another
let nulls = data.filter((item) => item[valueToOrderBy] == null)
let toSort = data.filter((item) => item[valueToOrderBy])
// Sort the non-null values
let sorted = toSort.sort((a, b) => {
// Check if both values are numeric
let aa = isNumeric(a[valueToOrderBy])
let bb = isNumeric(b[valueToOrderBy])
// If numerics
if (aa.isNum && bb.isNum) {
return aa.value - bb.value
}
// If strings
return (a[valueToOrderBy]).toLowerCase() > (b[valueToOrderBy]).toLowerCase() ? 1 : -1;
});
// The sorting direction
if (orderDirection === "desc") {
sorted.reverse();
}
// Add the nulls at the end of the returned array
return sorted.concat(nulls);
}
So to apply it in your React ponent return is:
sortAll(data, orderDirection, valueToOrderBy).map(...)
instead of:
sortedRowInformation(data, getComparator(orderDirection, valueToOrderBy)).map(...)
Squarely discard those functions: sortedRowInformation
, getComparator
and descendingComparator
and replace numeric
with my isNumeric
.
Here is a CodePen where I tested the sorting cases with some feak data
.
If it does not already work in all cases with your data
... At least, it is way easier to improve. ;)
It's unclear what's your sorting function is. Array.prototype.sort()
expects Comparator
in the form of
function Comparator(a, b, /* no `orderBy` here! */) { ... }
See docs
and passes items of an array in a
and b
, not two references to the same array. So the code your've wrote in this question looks wrong.
But maybe that's just a copy-paste error, so let's assume that your're using the standard Array.prototype.sort()
. In this case
if( a === null ) return 1
if( b === null ) return -1
should do the trick:
function numeric(x) {
const val = parseFloat(x);
if (isNaN(val)) {
return parseFloat(x.slice(1));
} else {
return val;
}
}
function descendingComparator(a, b) // removed `orderBy`
{
if( a === null ) return 1
if( b === null ) return -1
const numericA = numeric(a); // removed [orderBy]
const numericB = numeric(b); // removed [orderBy]
if (numericB < numericA){
return -1
}
if (numericB > numericA){
return 1
}
return 0
}
const test_arr = [null, 1, null, '3%', 0, '2', "$4", null]
console.log([...test_arr].sort(descendingComparator))
// here's how you can implement behavior of removed `orderBy` arg
const ascendingComparator = (a, b) => descendingComparator(b, a)
console.log([...test_arr].sort(ascendingComparator))
// or simply use `reverse()`
// or, if you want `null` at the and in this case too, then
const ascendingComparator2 = (a, b) => {
if( a === null ) return 1
if( b === null ) return -1
return descendingComparator(b, a)
}
console.log([...test_arr].sort(ascendingComparator2))
You can try something like below:
function numeric(x,order) {
const val = parseFloat(x);
if(!x) {
return order === 'desc'? -1 : 1;
}
else {
if (isNaN(val)) {
return parseFloat(x.slice(1));
} else {
return val;
}
}
}
本文标签: javascriptHow to handle null values inside my sort by functionStack Overflow
版权声明:本文标题:javascript - How to handle null values inside my sort by function? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744869510a2629542.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论