I am trying to apply the sorting method to one of the columns in my table. That column is having numerical values and empty values ("--"). I would like to sort the values in ascending and descending manner. And I want all the empty values should go to the bottom of the table in both sorting types.
Could anyone please suggest a custom sorting function to use with react-table-6?
Thank You...
It's crazy that you asked this because I just finished working on this exact problem with the exact same requirements -- lucky you!
This is in TypeScript, but you can remove the types if you want.
The columns:
const columns: Column[] = useMemo(
() => [
{
Header: 'Rank',
accessor: 'rank',
sortType: rankSort, // This is the rankSort() function below
maxWidth: 10,
},
...
],
[]
);
This is the ranking function:
const getRankValueString = (value: number, desc?: boolean): string => {
let stringValue = value.toString();
if (stringValue === '--') {
if (desc) {
stringValue = '-9999999';
} else {
stringValue = '9999999';
}
return stringValue;
}
for (let i = stringValue.length; i < 6; i++) {
stringValue = `0${stringValue}`;
}
return stringValue;
};
const rankSort = (
rowA: Row,
rowB: Row,
columnId: string,
desc?: boolean
): number => {
return getRankValueString(rowA.values[columnId], desc).localeCompare(
getRankValueString(rowB.values[columnId], desc)
);
};
It's a bit hacky, but it sorts values up to 6 digits. I'm open to optimizations.
Related
I have an array of objects (not primitives) called "streaks" on my state tree and I want to observe only the changes to that array, not simply emit the entire array every time it changes. When I try this using pairwise() I get two identical arrays every time, even though I thought pairwise() would join the previous version and the current version. Why is pairwise() sending two identical arrays? NOTE streaks[1] and streaks[0] are identical, so _.differenceBy() isn't finding any changes because the two arrays are the same.
import {from} from "rxjs";
import {map, pairwise} from "rxjs/operators";
import * as _ from 'lodash';
const state$ = from(store);
const streaks$ = state$.pipe(
map(state => state.streaks),
// distinctUntilChanged(), <-- i've tried this and nothing is output at all
pairwise(),
map(streaks => {
let diff = _.differenceBy(streaks[0], streaks[1], _.isEqual);
console.log('diff', diff); //<-- this is an empty array
return diff;
})
);
streaks$.subscribe((streaksArray) => {
console.log('STREAKS$ 0', streaksArray); //<-- this is never even hit
} );
I solved this issue by creating an distinctUntilChangedArray operator that uses a self written compareArray function
Create compare array function
const compareArray<T> = (first: T[], second: T[], comparator=: (obj: T, obj2: T) => boolean): boolean {
return (first.length === 0 && second.length === 0)
|| (first.length === second.length && first.every((value, index) => {
return comparator
? comparator(value, second[index])
: JSON.stringify(value) === JSON.stringify(second[index]);
}))
}
Maybe you find a better array comparator. This is just my personal implementation of it
Create distinctUntilChangedArray operator
const distinctUntilChangedArray<T> = (comparator?: (prev: T, curr: T) => boolean): MonoTypeOperatorFunction<T[]> {
return distinctUntilChanged((prev: T[], curr: T[]) => {
return compareArray(first, second, comparator);
}
}
Final usage
interface nonPrimitive {
id: number;
name: string;
}
const nonPrimitiveComparator = (prev: nonPrimitive, curr: nonPrimitive): boolean => {
return prev.id === curr.id && prev.name === curr.name;
}
const source$: Observable<nonPrimitive>;
const distinctedSource$ = source$.pipe(
distinctUntilChangedArray(nonPrimitiveComparator)
);
How i can write generic function, which take Array of Objects (any type of Object, possible even null and undefined), and filter it to return just valid items of array? If i write it lite this, i will lose genericity :/
// #flow
// Types
type Person = {
id: string,
name: string,
};
type Car = {
id: string,
color: string,
};
// Function definition
const isNotUndefinedOrNull = item => !(item === null || item === undefined);
export const trimList = (list: Array<any> | $ReadOnlyArray<any>): Array<any> => {
return list.filter(isNotUndefinedOrNull);
};
// Constants
const persons = [{ id: 'p1', name: 'Johny' }, null, undefined];
const cars = [{ id: 'c1', color: 'red' }, null, undefined];
// Calls
const trimmedPersons = trimList(persons);
const trimmedCars = trimList(cars);
PROBLEM is, there i have trimmed cars and persons, but flow doesnt know, there is Cars in the trimmedCars list and neither know there is Persons in trimmedPersons list. Flow see just Array and i dont know, how to write is right, to not lose this info.
Flow try
As flow has a bug with Refine array types using filter we use explicit type casting ((res): any): T[]).
function filterNullable<T>(items: (?T)[]): T[] {
const res = items.filter(item => !(item === null || item === undefined);
return ((res): any): T[]);
}
// Example
const a: number[] = filterNullable([1, 2, null, undefined]);
i found it :)
export function trimList<V>(list: Array<?V> | $ReadOnlyArray<?V>): Array<V> {
return R.filter(isNotUndefinedOrNull, list);
}
I'm new to flow, any trying to cover some of my functions, however often I have these snippets where I extract fields form an object based on some condition. But I'm struggling to cover them with flow.
const _join = function ( that: Array<Object>, by: string, index: number) {
that.forEach((thatOBJ: {[string]: any}, i: number)=>{
let obj: {[string]: any} = {};
for (let field: string in thatOBJ) {
if (field !== by) {
obj[`${index.toString()}_${field}`] = thatOBJ[field]; // NOT COVERED
} else {
obj[field] = thatOBJ[field]; // NOT COVERED
}
that[i] = obj;
}
});
}
The array that in this code is a data array so can really be in any format of mongodb data.
Any ideas on what to add to make the two lines which are not covered by flow covered?
Thanks.
A few notes...
This function has a "side effect" since you're mutating that rather than using a transformation and returning a new object.
Array<Object> is an Array of any, bounded by {}. There are no other guarantees.
If you care about modeling this functionality and statically typing them, you need to use unions (or |) to enumerate all the value possibilities.
It's not currently possible to model computed map keys in flow.
This is how I'd re-write your join function:
// #flow
function createIndexObject<T>(obj: { [string]: T }, by: string, index: number): { [string]: T } {
return Object.keys(obj).reduce((newObj, key) => {
if (key !== by) {
newObj[`${index}_${key}`] = newObj[key]
} else {
newObj[key] = obj[key]
}
return newObj
}, {})
}
// NO ERROR
const test1: { [string]: string | number } = createIndexObject({ foo: '', bar: 3 }, 'foo', 1)
// ERROR
const test2: { [string]: string | boolean } = createIndexObject({ foo: '', bar: 3 }, 'foo', 1)
I'm struggling to find a solution to this.
I have the following model, loaded via a promise thru a service.
export class Something {
id: number;
someTime: string;
items: [
{
id: number;
description: string;
unit: string;
portion: number;
std: number;
}
]
}
It has mock data:
const SOMETHINGS: Something[] = [
{
id: 0,
someTime: 'Now',
items: [
{
id: 0,
description: 'A',
unit: 'ml',
portion: 275,
std: 64
},
{
id: 1,
description: 'B',
unit: 'g',
portion: 50,
std: 378
},
....
]
}
]
The actual values are irrelevant, but I need to access each portion and std values. Multiply them and finally SUM them together.
Like so:
275 * 64 = 17600
50 * 378 = 18900
total = 36500
Inside my component I have assigned the returned data to a local array like so.
something: Something[] = [];
constructor( private somethingService: SomethingService ) {}
ngOnInit(){
this.getSomething();
}
getSomething(){
this.somethingService.getSomething().then(something => this.something = something);
}
This value is not inside a repeated section of my template so I can't use an *ngFor type approach.
It would seem logical to use something like:
calcTotal(){
let totals:array<Number>;
let gross:number = 0;
this.something.forEach((items) => total.push( items.portion * items.std ));
totals.forEach((total:number) => gross += total));
return {
total: totals;
}
}
But this doesn't work..
I simply don't know how to access each item and extract the two values, multiply them, save them for later extract the next pair and so on. Then finally take all the results and sum them up and pass it to the view.
Any help would be appreciated.
Steve
you could do this when you get your data and subscribe to it from your service.
this.somethingService.getSomething()
.then((data) => {
this.data = data;
this.totals = this.calculateValues(data);
});
// With the model you have up there it would look like this.
calculateValues(data){
let total = 0;
data.map( somethingObj => {
somethingObj.items.map(itemObj => {
total += (itemObj.portion * itemObj.std);
})
})
return total;
}
Hello Every One!!!
I added codes for getting unique dropdownlist in the columns of jqgrid .
Dropdownlist is coming but it is coming for the first page of the jqgrid means that dropdownlist has the unique values of the first page of the jqgrid whereas i need all the unique values of the whole Jqgrid..
Below I am posting my codes...
grid = $("#gridId");
getUniqueNames = function (columnName) {
var texts = grid.jqGrid('getCol', columnName), uniqueTexts = [],
textsLength = texts.length, text, textsMap = {}, i;
for (i = 0; i < textsLength; i++) {
text = texts[i];
if (text !== undefined && textsMap[text] === undefined) {
// to test whether the texts is unique we place it in the map.
textsMap[text] = true;
uniqueTexts.push(text);
}
}
return uniqueTexts;
},
buildSearchSelect = function (uniqueNames) {
var values = ":All";
$.each(uniqueNames, function () {
values += ";" + this + ":" + this;
});
return values;
},
setSearchSelect = function (columnName) {
grid.jqGrid('setColProp', columnName,
{
stype: 'select',
searchoptions: {
value: buildSearchSelect(getUniqueNames(columnName)),
sopt: ['eq']
}
}
);
};
This function i have called like this...
setSearchSelect('extension');
grid.jqGrid('setColProp', 'Name',
{
searchoptions: {
sopt: ['cn'],
dataInit: function (elem) {
$(elem).autocomplete({
source: getUniqueNames('Name'),
delay: 0,
minLength: 0
});
}
}
});
setSearchSelect('username');
grid.jqGrid('setColProp', 'Name',
{
searchoptions: {
sopt: ['cn'],
dataInit: function (elem) {
$(elem).autocomplete({
source: getUniqueNames('Name'),
delay: 0,
minLength: 0
});
}
}
});
In between these two code snippets I am loading data into jqgrid locally using Ajax call.
Any help will be heartely appreciated..
Thanx in advance..
I belive getCol will return only currently loaded data from jqgrid (for defined column).
Because at first you load just first page, autocomplete has no way of knowing unique values of column for more than that!
You will have to either load all pages at once (small dataset) or
fill autocomplete from database.