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.
Related
So i'm building a calculator/estimator that is basically just a more complicated version of this margin calculator: https://www.omnicalculator.com/finance/margin
So here's one edge case that I'm trying to fix right now.
My costs are broken into 3 parts due to outsourced data- labor, material and laborAndMaterial. LaborAndMaterial is the sum of labor and material, but it can be the only known cost factor so that's why it's broken into 3 parts.
So here's the problem. Say we know that laborAndMaterial is set to 100 and labor and material are 0
cost: {
labor: 0,
material: 0,
laborAndMaterial: 100
}
Then the user enters 50 for labor. Because laborAndMaterial is 100 and labor is now 50 we can autofill material to 50.
But what's happening right now as the user is typing "50" it autofills material to 95 as the user types the 5 in 50. Then when they enter the "0" it sets the laborAndMaterial to 145 (50 + 95). But in that example I need to adjust how I autofill material to continue to update as the user enters more numbers (labor = 5 -> 50 -> 506) (material = 95, 50, -406). As of now I basically have my formula run like:
if(key === "cogs.labor") {
if(laborAndMaterial > 0) {
params["cogs.material"] = laborAndMaterial - value // value is what was entered
}
}
But I still need to allow for the other edge cause that as labor is entered and material is known it updates the laborAndMaterial value
cost {
labor: 50,
material: 50,
laborAndMaterial: 100
}
So if someone enters 100 for labor and we know material is 50 we can autofill laborAndMaterial to 150.
So I have something like:
if(material > 0) {
params["cogs.laborAndMaterial"] = material + value // value is what was entered
}
Any ideas how I can tweak my formula to decide the autofill and continue to update that paramater while still allowing for multiple edge cases?
The margin calculator from omnicalculator is a good example as they solved the issue, but I've been scratching my head over it for some time.
I think you basically need to differentiate between which cost centers are treated as input and which are treated as output. So when you start, each piece of data you're provided is input, and the data you use to autofill the rest of the form is output.
As the user types, any information they give is then treated as input data. Given that any two values can be used to calculate the third, you can only have two of the fields be treated as input at a time.
Here's a code sample to get an idea of what I mean:
// This is a queue to hold your two input cost centers
const inputFields = [];
// Determine the odd one out that we need to calculate
function getVariableCostCenter() {
if (!inputFields.includes('labor')) {
return 'labor';
}
if (!inputFields.includes('material')) {
return 'material';
}
return 'laborAndMaterial';
}
function calculateCostCenters(costCenters) {
const variableCostCenter = getVariableCostCenter();
if (variableCostCenter === 'labor') {
return {
...costCenters,
labor: costCenters.laborAndMaterial - costCenters.material,
};
}
if (variableCostCenter === 'material') {
return {
...costCenters,
material: costCenters.laborAndMaterial - costCenters.labor,
};
}
return {
...costCenters,
laborAndMaterial: costCenters.labor + costCenters.material,
};
}
function initializeCostCenters(costCenters) {
// First, we determine which field(s) are known up front
if (costCenters.labor > 0) {
inputFields.push('labor');
}
if (costCenters.material > 0) {
inputFields.push('material');
}
if (costCenters.laborAndMaterial > 0 && inputFields.length < 2) {
inputFields.push('laborAndMaterial');
}
// ...then do whatever you normally do to populate the form
}
function updateCostCenters(previousCostCenters) {
// Update the input fields so that the user's input
// is always treated as one of the two input fields
if (!inputFields.includes(key)) {
inputFields.shift();
inputFields.push(field);
}
const costCenters = calculateCostCenters({
...previousCostCenters,
[key]: value,
});
params['cogs.labor'] = costCenters.labor;
params['cogs.material'] = costCenters.material;
params['cogs.laborAndMaterial'] = costCenters.laborAndMaterial;
}
Pretty roughly it might look like below.
Note that I remembering last touched fields, which are became "fixed", because we can not recalculate values circularly.
Also, note that I use direct value update, while in some frameworks/libs it might generate change/input event, so you would want to set values silently.
setup = {
labor: {
value: 0
},
material: {
value: 0
},
laborAndMaterial: {
value: 100
}
};
// the number which we are treat as "fixed", may be changed later
let prevFixed = 'labor';
let fixed = 'labor';
const calculateTheRest = () => {
if (!setup.material.touched && !setup.laborAndMaterial.touched ||
!setup.labor.touched && !setup.laborAndMaterial.touched ||
!setup.labor.touched && !setup.material.touched) {
return false; // two unknowns, can't recalculate
}
if (!setup.labor.touched || fixed !== 'labor' && prevFixed !== 'labor') {
setup.labor.value = setup.laborAndMaterial.value - setup.material.value;
} else if (!setup.material.touched || fixed !== 'material' && prevFixed !== 'material') {
setup.material.value = setup.laborAndMaterial.value - setup.labor.value;
} else {
setup.laborAndMaterial.value = setup.material.value + setup.labor.value;
}
}
const $els = {};
Object.keys(setup).forEach(key => $els[key] = document.querySelector('#' + key))
const onInputChanged = (e) => {
const key = e.target.id;
setup[key].value = +e.target.value;
setup[key].touched = true;
if (fixed !== key) {
prevFixed = fixed;
fixed = key;
}
calculateTheRest();
Object.keys(setup).forEach(key => $els[key].value = setup[key].value);
}
Object.keys(setup).forEach(key => {
$els[key].value = setup[key].value; // initial set
setup[key].touched = setup[key].value !== 0; // 0 on initial setup are the numbers that not set
$els[key].addEventListener('input', onInputChanged);
})
<p><label>labor: <input id="labor" type="number"/></label></p>
<p><label>material: <input id="material" type="number"/></label></p>
<p><label> laborAndMaterial: <input id="laborAndMaterial" type="number" /></label></p>
I think you need to implement a condition on your laborAndMaterial field.
Check the condition: -
if(labor > 0 && material > 0){
let laborAndMaterial = labor + material;
}
And after that set the laborAndMaterial variable value into field,
I think it will may help you.
I have data from an movie-api I want to sort based on a select menu, either by year in descending order or title in alphabetical order.
Although Im only updating state in the sort function, not using the variable any where, the data thats already mapped out, and in a different array, updates accordingly. I guess its somehow related to the first change in state for each of the two differen variables?
Any idea how I should solve this correctly and why this is happening?
const sortData = (e) => {
if (e === "year"){
const yearData = data.sort(function(a, b) {
const yearA = a.Year;
const yearB = b.Year;
if (yearA < yearB) {
return -1;
}
if (yearA > yearB) {
return 1;
}
return 0;
});
setYearData(yearData);
}
if (e === "title") {
const titleData = data.sort(function(a, b) {
const titleA = a.Title.toUpperCase();
const titleB = b.Title.toUpperCase();
if (titleA < titleB) {
return -1;
}
if (titleA > titleB) {
return 1;
}
return 0;
});
setTitleData(titleData);
}
}
The sort() method sorts the elements of an array in place, so the data(state) changed without using setState (It may cause some unpredictability happened in the execution)
You can use the sort() method on a copy of the array, so it doesn't affect your state array, I guess this change may work:
use [...data].sort(...) instead of data.sort(...)
Array.sort(), in your case data.sort() updates the original array in addition to returning it. Seems like your data variable is some sort of global that gets changed during sort().
I have a dynamic object that consists of the number of occurrences from a user selection. The object looks like this:
{ "Excitement": 2, "Competence": 3, "Sophistication": 1 }
This is the function:
rankFactors() {
const headers = this.headers;
var counts = {};
for (var i = 0; i < headers.length; i++) {
var num = headers[i];
counts[num] = counts[num] ? counts[num] + 1 : 1;
}
return counts;
}
How do I sort this object so that it is always in descending order? That way I can print it as a 'Top 3' list.
This is my CodeSandbox: https://codesandbox.io/embed/vue-template-mzi03
To reproduce just make selection of personality traits, with multiple options from a few headers.
I think I'd do it something like this:
rankFactors() {
const headers = this.headers;
const counts = {};
for (const header of headers) {
counts[header] = (counts[header] || 0) + 1;
}
const factors = Object.keys(counts).map(header => {
return {
name: header,
count: counts[header]
}
});
factors.sort((a, b) => b.count - a.count);
return factors;
}
The first stage is very similar to what you had, building up an object of counts. That's an easy data structure to work with for gathering those counts but once that stage is done it's not a great choice for dealing with sorting. For that we're better off with an array.
So next up it converts the object into an array of objects, each of the form {name: 'Excitement', count: 2}. This array is then sorted based on the counts and then returned. You could throw in a .slice(0, 3) if you just want the top 3.
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.
I have a variable sequence number in my Data.model . sequence number can be greater or equal to -1. i would like to display the store values (whose sequence number are greater than or equal to 0) sorted in ascending Order.this sorting should ignore records with sequence number -1 and display that at the last.
You have to use sorters confing of your store and custom function to sort with sorterFn property.
Working fiddle
sorters: [{
sorterFn: function(record1, record2) {
var data1 = record1.get('sortValue'),
data2 = record2.get('sortValue');
if(data1 === -1) {
return 1;
}
if(data1 === data2) {
return 0;
} else {
if(data1 < data2) {
return -1;
} else if (data1 > data2) {
return 1;
}
}
}
}],