How to add case insensitive sorting in react table? - reactjs

By default, react table sorting is case sensitive.
In order to make it insensitive we have to write a custom sort function.
I like this answer from https://github.com/react-tools/react-table/issues/335.
This would help.

For case insensitive and numbers sorting pass sortTypes in table props like this:
useTable({
sortTypes: {
alphanumeric: (row1, row2, columnName) => {
const rowOneColumn = row1.values[columnName];
const rowTwoColumn = row2.values[columnName];
if (isNaN(rowOneColumn)) {
return rowOneColumn.toUpperCase() >
rowTwoColumn.toUpperCase()
? 1
: -1;
}
return Number(rowOneColumn) > Number(rowTwoColumn) ? 1 : -1;
}
}
})

//function to sort the results
function filterCaseInsensitive(filter, row) {
const id = filter.pivotId || filter.id;
return (
row[id] !== undefined ?
String(row[id].toLowerCase()).startsWith(filter.value.toLowerCase())
:
true
);
}
// react table code goes here
<ReactTable
data={data}
columns={columns}
filterable
defaultFilterMethod={(filter, row) => filterCaseInsensitive(filter, row) }
/>

The question mentions sorting but links to filtering. For custom sorting the app's owner mentions on Github to pass a sortTypes: { alphanumeric: MyCustomFunc } in the table props, like this:
useTable({
columns,
sortTypes: {
alphanumeric: (row1, row2, columnName) => {
return compareIgnoreCase(
row1.values[columnName],
row2.values[columnName]
)
},
},
},
useSortBy
)

The proper way to sort a column with react-table and with a case-insensitive approach, would be to use sortType on a column and then provide a custom function.
/**
*
* React Table helper to sort tables
* with case-insensitive support
*
*/
export const customInsensitiveCompare = (
rowA: Row,
rowB: Row,
columnId: string,
desc: boolean
) => {
const valueA = rowA.values[columnId].toLowerCase();
const valueB = rowB.values[columnId].toLowerCase();
if (desc) {
return valueA.localeCompare(valueB) > 0 ? 1 : -1;
}
return valueB.localeCompare(valueA) > 0 ? -1 : 1;
};
If you do not use Typescript just remove the types and everything will work fine. Take care that we need to return only -1 or 1 and localeCompare sometimes can return 0 or even different values according to MDN docs, that why we assign only -1, 1 as react-table expects.

(firstRow, secondRow, accessor) => {
// get from lodash
const firstValue = get(firstRow.values, accessor, '');
const secondValue = get(secondRow.values, accessor, '');
return firstValue > secondValue ? 1 : -1;
}
Above is the solution I used to assign to "alphanumeric", which covered both cases. Because, as in many programming languages, strings are compared lexicographically hence we need not change the case. And even comparing with numbers works fine.

Related

What's the type of arguments in sort method (typescript and react)

const [search] = useSearchParams()
const sort = search.get('sort')
export interface Note {
id?: string | any
author: string
title: string
category: string
description: string
favourite: boolean
date: string
descLength: number
}
const sortedNotes = (notes: Note[]) => {
type sortProps = {
a: Note
b: Note
}
return notes.sort(({ a, b }: sortProps) => {
const { favourite, descLength, id } = a
const { favourite: favouriteB, descLength: descLengthB, id: id2 } = b
switch (sort) {
case 'favourite':
return favourite > favouriteB ? 1 : -1
case 'longest':
return descLength > descLengthB ? 1 : -1
case 'none':
return id - id2 ? 1 : -1
default
}
})
}
const sortingNotes = sortedNotes(notes)
const currentNotes = sortingNotes.slice(indexOfFirstNote, indexOfLastNote)
I have tried to create a sort system in a note app but I don't know what the expected argument is
Please check a photo, i dont know why that error is occured
Your first issue is in the arguments you are passing to notes.sort(). You are giving a single object sortProps which contains two fields, rather than giving it two objects.
//incorrect
notes.sort(({ a, b }: sortProps) => {})
//correct
notes.sort((a: Note, b: Note) => {})
The type sortProps should not be necessary any longer.
Your method also does not return anything (in effect, it returns undefined) when the switch statement executes its default code. Even if you don't think this will ever occur in practice, TypeScript forces you to either ensure this will happen via the type system or to put some valid default value in place.
const sortedNotes = (notes: Note[]) => {
return notes.sort((a: Note, b: Note) => {
const { favourite, descLength, id } = a
const { favourite: favouriteB, descLength: descLengthB, id: id2 } = b
switch (sort) {
case 'favourite':
return favourite > favouriteB ? 1 : -1
case 'longest':
return descLength > descLengthB ? 1 : -1
default:// includes case 'none'
return id - id2 ? 1 : -1
}
})
}
See the documentation.
Sort takes two arguments, a and b. You're passing it a single argument-- an object with a and b within the object.
Instead of
notes.sort(({ a, b }: sortProps) => {
Try
notes.sort((a, b) => {
Though I will say that this is the sort of thing more appropriate for consulting documentation for as opposed to posting a question. The documentation on all the built in methods in JavaScript is quite extensive.

react check array values based on specific keys returning true/false if values found

I am trying to figure out how to do this but can't seem to wrap my head around it..
I have an address object
const obj = {
"address_type":"Home",
"country":"US",
"addressLine1":"123 Any Street",
"addressLine2":"",
"city":"Any Town",
"state":"Indiana",
"state_code":"IN",
"zip":"46220-4466",
"phone":"6715551313",
"mobile_number":"",
"extn":"",
"fax":"",
"county_name":"MyCounty"
}
I want to check for any key that has a value but only specific keys
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
I want to check all keys in objProps against my address object and if any one of them contains a value return true (doesn't matter if its 1 or all 6).. If all keys don't contain a value then return false (Sometimes I will get an address object that has all null values)
I've tried various ways to accomplish this but have failed in each one.
The variation I am working on now is using reduce. While it doesn't meet my needs I thought I could check the resulting array and if length was greater than 0 than I have my answer..
Work-in-progress:
function hasAddressData(obj: any) {
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
const keysWithData = objProps.reduce((accumulator, key) => {
const propExistsOnObj = obj.hasOwnProperty(key);
let keyHasData = [];
if (obj[key].length > 0 ) {
keyHasData = obj[key]
}
if (!propExistsOnObj) {
accumulator.push(key);
} else if (keyHasData) {
const equalValueKeyIndex = accumulator.indexOf(key);
accumulator.splice(equalValueKeyIndex, 1);
}
return accumulator;
});
return keysWithData;
}
The above is messed up I know and doesn't work.. Just learning this stuff.. anyone have a suggestion or comment?
Check that .some of the objProps, when looked up on the obj, contain a value. (Either with Boolean or by comparing against '')
const obj = {
"address_type":"Home",
"country":"US",
"addressLine1":"123 Any Street",
"addressLine2":"",
"city":"Any Town",
"state":"Indiana",
"state_code":"IN",
"zip":"46220-4466",
"phone":"6715551313",
"mobile_number":"",
"extn":"",
"fax":"",
"county_name":"MyCounty"
}
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
const somePopulated = objProps.some(prop => obj[prop]);
// or prop => obj[prop] !== ''
console.log(somePopulated);
const obj = {
"address_type":"Home",
"country":"US",
"addressLine1":"",
"addressLine2":"",
"city":"",
"state":"Indiana",
"state_code":"",
"zip":"",
"phone":"6715551313",
"mobile_number":"",
"extn":"",
"fax":"",
"county_name":""
}
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
const somePopulated = objProps.some(prop => obj[prop]);
// or prop => obj[prop] !== ''
console.log(somePopulated);
function checkKeys(target, props) {
return props.some((prop) => {
return target.hasOwnProperty(prop) && target[prop];
});
}
Explanation: some iterates through the props you want to check, returning true immediately when one is found (i.e. the callback returns true). If no props are found (i.e. no callback returns true), some returns false.
hasOwnProperty ensures that you are only checking properties on target, and not looking up the prototype chain. target[prop] checks for a truthy value. You may need to modify this last check if you're going to be handling values other than strings.

Search/Filter a table in React: Replace null values with an empty string

I made a simple search function for filtering through a datatable filled with JSON objects from an API.
It works for most tables in my application but will crash it if any of the objects in the table have a null value.
my search function looks like this:
const search = (rows) => {
const columns = rows[0] && Object.keys(rows[0]);
return rows.filter((row) =>
columns.some(
(column) =>
row[column]
.toString()
.toLowerCase()
.indexOf(q.toString().toLowerCase()) > -1
)
);
};
I just want to replace any null values in the objects with an empty string.
Can anyone help me with this in a simple way?
Try this:
let newRows = rows.map((row) =>
columns.map(
(column) => columns || "" // return column if non empty else return ""
));
};
console.log("newRows", newRows) // rows with empty string

React table sort and search

i have data like
const data =[
{
name:'gacvuc',
pot1[
{
density:1
}
]
pot2[
{
density:2
}
]
}
.....
]
now i made a toggle in that do we wanna see pot1 density or pot2 density
and made a table with that like this
<tablebody>
data.map(a,in)=>{
const show= toggleChoice === 0 ? a.pot1 : a.pot2;
}
return(
<tablecell>{a.name}</tablecell>
<tablecell>{show.density}</tablecell>
</tablebody>
);
now what i wanna do is i wanna sort by density in ascending or descending or search by it's name can anyone show me how to do that
First, I would recommend taking a look at the format of your data and looking for mistakes.
To sort your data, you can simply use the built-in sort method and pass in a function that gives instructions on how to sort two different objects in your data.
data.sort(function(a, b) {
// you can define this based on your toggle variable
const pot = "pot1";
// densities of both object
const aDensity = a[pot][0].density;
const bDensity = b[pot][0].density;
// compare them and then return -1, 0, or 1 accordingly
if (aDensity == bDensity) { // if they are equal, return 0
return 0;
}
else if(aDensity > bDensity) { // > return 1
return 1;
}
else { // < return -1
return -1;
}
})
In this snippet, you get the densities of two arbitrary objects, a and b, and then return -1, 0, or 1 depending on the values of the density. In this example. I sorted them in ascending order, but if you want descending, you could swap the 1 for -1 and vice versa in the code.

React not re-rendering when state changed from hook

Edit: Thanks for the help everyone. I needed to change the reference of the array and fixed it by doing:
setData([...sorted])
I am currently rendering out a list of tasks. This is a snippet of my return function within a functional component:
const [ data, setData ] = useState( mockData )
<tbody>
{ data.map(d => <TaskItem key={d.claimable} task={d}/>) }
</tbody>
When I click on a certain button on the page, the dataset gets sorted and I call setData(sortedData)
For some reason, the table isnt being re-rendered with the sorted data. Is there something I did wrong here?
This is the sort function:
function filterByContactAmount():void {
let sorted = data.sort((a:any, b:any) => {
let aTimesContacted:number = a.data.person.contact.list.reduce((acc:number, val:any):number => acc + val.history.length, 0)
let bTimesContacted:number = b.data.person.contact.list.reduce((acc:number, val:any):number => acc + val.history.length, 0)
if ( aTimesContacted > bTimesContacted ) {
return 1
}
if ( bTimesContacted > aTimesContacted ) {
return -1
}
return 0;
})
console.log(sorted)
setData(sorted)
}
Its because you are using the same ref of the array, you need set the new data with
setData(old => "sorted data");
to change the reference of the state and it updates
function filterByContactAmount():void {
let sorted = data.sort((a:any, b:any) => {
let aTimesContacted:number = a.data.person.contact.list.reduce((acc:number, val:any):number => acc + val.history.length, 0)
let bTimesContacted:number = b.data.person.contact.list.reduce((acc:number, val:any):number => acc + val.history.length, 0)
if ( aTimesContacted > bTimesContacted ) {
return 1
}
if ( bTimesContacted > aTimesContacted ) {
return -1
}
return 0;
})
console.log(sorted)
setData(old => [...sorted]) // Sorted is the new state sorted
}
You are mutating sate, the other answer is probably not the best because you are still mutating state and then setting state with a copy of the already mutated value.
The sort function can also be optimized. Maybe try the following:
function filterByContactAmount() {
let sorted = data
.map(d => ({//map shallow copies the array
...d,//shallow copies the item
sortedNum: d.data.person.contact.list.reduce(//do this once for every item, not for every time sort callback is called
(acc, val) => acc + val.history.length,
0
),
}))
.sort((a, b) => a.sortedNum - b.sortedNum);
console.log(sorted);
setData(sorted);
}
I think issue is located under d.claimable, I suppose it is boolean variable type. You must know that every key prop must be unique. Check if you have for example.id property, if not add it.
Uniqueness of key prop is very important during reconciliation process.
Unique identifier with a group of children to help React figure out which items have changed, have been added or removed from the list. It’s related to the “lists and keys” functionality of React described here.
Very nice article about reconciliation.
<tbody>
{ data.map(d => <TaskItem key={d.claimable} task={d}/>) }
</tbody>

Resources