Arrays.sort comparator issue [duplicate] - javascript

This question already has answers here:
Sorting an array of objects by property values
(35 answers)
Closed 4 years ago.
Issue
Hello, Thanks ahead for anyone willing to help me, I have a problem with sorting an object array in js.
In this component I keep an array inside this.state of id, hours, day, and active.
My issue is when I try to sort the object.hours array the function won't sort by my logics and understanding of this comparator and the array stays same even if the sort expression is false.
Which can be found under sortHoursByTime declaration.
Expected Behavior
if (obj1.toTime < obj2.fromTime) then sort the array where obj1 is first in order.
Code:
export default class SetWorkingHours extends Component {
constructor() {
super();
this.state = {
workingDays: ['א׳', 'ב׳', 'ג׳', 'ד׳', 'ה׳', 'ו׳'].map((day, _id) => ({
_id,
name: day,
hours: [],
active: false,
})),
activeButton: -1,
}
}
static defaultProps = {};
sortHoursByTime(day) {
let sortedDays = day;
sortedDays.hours.sort((obj1,obj2) => obj1.fromTime < obj2.toTime);
this.setState({workingDays: sortedDays});
}
appendWorkingHours = (hours) => {
let dateHours = {
fromTime: this.formatDateToHourString(new Date(hours.fromDate)),
toTime: this.formatDateToHourString(new Date(hours.toDate))
};
let selectedWorkingDays = this.state.workingDays;
selectedWorkingDays.forEach((day) => {
if (day.active && this.isHoursValid(day, dateHours)) {
day.hours.push(dateHours);
this.sortHoursByTime(day)
}
});
this.setState({workingDays: selectedWorkingDays})
} // Editor's note: the closing curly brace of appendWorkingHours method
// was missing, so I added this line.
};
Environment
react-native -v: "0.54.2"
node -v: v9.8.0
npm -v: 5.6.0
yarn --version: 1.5.1
target platform: iOS
operating system: OSX

When using Array.sort in JavaScript with a custom sorting function you need to return -1 less then, 0 equals, 1 greater then.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
This should fix your issue.
sortHoursByTime(day){
let sortedDays = day;
// Switch from < to -
sortedDays.hours.sort((obj1,obj2) => obj1.fromTime - obj2.toTime);
this.setState({workingDays: sortedDays});
}

Related

What is a fast sort algorithm for a very, very long array of objects in JavaScript? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed last year.
Improve this question
I have been trying to sort an array with 2000 elements in ReactJS using JavaScript. The array looks like this:
data = [
{
index: 0,
id: "404449",
product_name: "ette",
brand_name: "Dyrberg/Kern",
base_price: "55.000",
actual_price: "55.000",
filename:
"http://images.booztx.com/dyrbergkern/400x523/329679_ette_sg_crystal.jpg",
},
{
index: 1,
id: "414661",
product_name: "braided mobile chain",
brand_name: "Octopus",
base_price: "44.900",
actual_price: "44.900",
filename: "http://images.booztx.com/octopus/400x523/SC09-750MU.jpg",
},
]
I tried sorting it by base_price with Array.sort( ) of JavaScript, like this:
data.sort((a, b) => {
return parseFloat(a.base_price) - parseFloat(b.base_price);
});
but since the array is very long, it has 2000 elements it takes a very long time to sort. It takes about 4 minutes. Does anyone have any solutions?
It turns out that the problem was using imports incorrectly. I imported the data as a constant, but array.sort( ) was trying to change it. I used a local variable for the result of sort and that fixed the problem.
import { data } from "assets/data/productList";
export const sortItems = (sortMode) => {
const SoretdData = data.sort((a, b) => {
if (sortMode === SortModes[1].type) {
return parseFloat(a.base_price) - parseFloat(b.base_price);
} else {
return parseFloat(b.base_price) - parseFloat(a.base_price);
}
});
return SoretdData;
};
As mentioned in the comments, what you're describing should not actually take very long. Here's an example, and on my machine I'm seeing less than 10 milliseconds of runtime for a 2000 element array:
const createFakeProduct = () => {
const priceNumber = Math.random() * 100;
const priceString = priceNumber.toFixed(3);
return {
index: 0,
id: "12345",
product_name: "hello world",
brand_name: "foo",
base_price: priceString,
actual_price: priceString,
filename: "abc123.jpg",
}
}
const data = [];
const len = 2000;
for (let i = 0; i < len; i++) {
data.push(createFakeProduct());
}
const before = Date.now();
data.sort((a, b) => {
return parseFloat(a.base_price) - parseFloat(b.base_price);
});
console.log(`elapsed: ${Date.now() - before} milliseconds`);
There must be something additional thing besides what you've shared that's slowing it down.

Set state based on value of array item in previous state

Hello I am new to programming and I am trying to make a function in React that adds a team to an array of selected teams but only if there are less than 2 teams selected and if the team is available (not already chosen). It is working to limit the array to only 2 values but the array will accept the same team twice and I am not sure why. For example if the user clicks ATL twice then that team will be in the array twice. What did I do wrong here and what should I change in order to fix it? Sorry if the question is too simple for this forum, I am new.
Here is the code where I am changing the state and checking if gameState.selected_teams[0] != team:
function App() {
const [gameState, setGameState] = useState({
cards: Cards,
selected_teams: []
});
function setTeam(team){
if (gameState.selected_teams.length < 2 && gameState.selected_teams[0] != team) {
setGameState((prevState) => ({
cards: prevState.cards,
selected_teams: [
...prevState.selected_teams, {
team
}
]
}))
}
}
And for the component that calls the setTeam function:
function TeamSelect({cards, setTeam}) {
var teams = Object.keys(cards);
return (
<div className='flex-container'>
<div className='team-container'>
{
teams.map(team => (
<div
onClick={() => setTeam(team)}
className='team-label' key={team}>
{team}
</div>
))
}
</div>
</div>
)
}
You're adding an object {team: team} to your selected teams array each time you perform your click here:
selected_teams: [
...prevState.selected_teams, {
team
}
]
but your team key that you pass into your setTeam function is a string, so your comparison fails as you're trying to compare a string with an object. You can change your comparison to extract the team property from your object:
gameState.selected_teams[0]?.team != team
The ? ensures that a value exists at index 0 in your array before using .team on it (otherwise you would get an error if it's undefined).
You can adapt this code to handle more than one object by using .every() to check that all objects in selected_team's aren't equal to the one you're adding:
if(gameState.selected_teams.length < 2 && gameState.selected_teams.every(obj => obj.team != team)
If you don't need to pass an object {team: team} (as an object with one property doesn't add much value), then you can simply push your team string into your selected teams, and use .includes() to check if the team you're adding already exists in the array:
selected_teams: [
...prevState.selected_teams, team
]
you can then update your condition to use .includes():
if(gameState.selected_teams.length < 2 && !gameState.selected_teams.includes(team))

Vue JS - Loop through a computed property to display a list of years

I currently have the following code that lists a list of years. I feel that all this code may be very unnecessary and perhaps a computed property for validYears, would help me make this code more optimal and get rid of the unnecessary watchers. My issue is converting this to a computed property as I'm failing to grasp the correct logic to achieve this. I'd appreciate if someone can offer an example of how I can set a computed property for valid years and still return the same result.
onBeforeMount(calculateDateRange)
watch(() => props.earliestDate, (newValue, prevValue) => {
calculateDateRange();
});
// If there is a new value passed from the parent, the dropdown should display that new value.
watch(() => props.latestDate, (newValue, prevValue) => {
calculateDateRange()
});
const validYears = ref([])
function calculateDateRange () {
for(let year = props.latestDate; year >= props.earliestDate; year--){
validYears.value.push(year)
}
}
I didn't provide the rest of the code not to clutter the question, but as one can see in this component I have a set of props that determine the values in my for loop.
You could optimize it as follows :
const validYears = computed(()=>{
let _years=[]
for(let year = props.latestDate; year >= props.earliestDate; year--){
_years.push(year)
}
return _years;
})

Check whether the same value is existing in array of objects in React [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I have inputs and a data table. When user fill the inputs and click add button, those new values add to the table. But user can not add same values if the value is already added to the table (same dates and value). Please find the screenshot below.
I have no idea how to do this. I found Set method but it's not working as I expected.
serviceFeeTableData array stores the table data values as objects.
this.state = {
periodFrom: new Date(),
periodTo: new Date(),
serviceFeeType: 'Percentage',
serviceFee: '',
refundableStatus: 'Refundable',
serviceFeeTableData: []
};
Add button function
addNewServiceFee = () => {
let resultData = [...this.state.serviceFeeTableData]
let result = Array.from(new Set(resultData))
return alert('Error! You have already added this value', result)
}
I need to check periodFrom date, periodTo date and serviceFee value is existing in the table record ( in the objecs of serviceFeeTableData ). If user already added that record to table, I need to show a error message. User can't add same exact record again and again. Only one serviceFee can add for the particular time period.
Input field values which is user entered, set to state.
addNewServiceFee function will take the state values and assign them to array as an object. I didn't add the code because of keep the code simple.
You can use the Array includes method if it's not nested object. Eg
var array1 = [1, 2, 3];
console.log(array1.includes(2));
//return true
If its nested object :
function checkObjectExist(obj, list) {
return list.some(elem => elem === obj) //condition to check exist or not
}
Note : You have not clearly mentioned what data you want to check. I am assuming id here. So the code should be like this :
addNewServiceFee = (e) => {
let resultData = [...this.state.serviceFeeTableData]
let checkIsExist = resultData.some(data => data.id === "testingcondition");
if(checkIsExist){
let result = resultData.filter(data => data.id === "testingcondition");
return alert('Error! You have already added this value', result)
}
}
Looping the array and find whether same values are there. If not, add the new record. If yes, notify an error.
let dateFormat = require('dateformat');
for (let a = 0; a < serviceFeeTableData.length; a++) {
let periodFrom = dateFormat(new Date(serviceFeeTableData[a].periodFrom), 'dd-mmm-yyyy')
let periodTo = dateFormat(new Date(serviceFeeTableData[a].periodTo), 'dd-mmm-yyyy')
if (periodFrom === obj1.periodFrom && periodTo === obj1.periodTo) {
statedate = false;
break;
}
}
if (statedate) {
serviceFeeTableData.push(obj1)
} else {
this.notifyError("Selected dates are overlapping with the existing periods. Please select a different period.");
}
}

JavaScript/React Native array(objects) sort

I'm starting with react-native building an app to track lap times from my RC Cars. I have an arduino with TCP connection (server) and for each lap, this arduino sends the current time/lap for all connected clients like this:
{"tx_id":33,"last_time":123456,"lap":612}
In my program (in react-native), I have one state called dados with this struct:
dados[tx_id] = {
tx_id: <tx_id>,
last_time:,
best_lap:0,
best_time:0,
diff:0,
laps:[]
};
This program connects to arduino and when receive some data, just push to this state. More specific in laps array of each transponder. Finally, I get something like this:
dados[33] = {
tx_id:33,
last_time: 456,
best_lap: 3455,
best_time: 32432,
diff: 32,
laps: [{lap:1,time:1234},{lap:2,time:32323},{lap:3,time:3242332}]
}
dados[34] = {
tx_id:34,
last_time: 123,
best_lap: 32234,
best_time: 335343,
diff: 10,
laps: [{lap:1,time:1234},{lap:2,time:32323},{lap:3,time:3242332}]
}
dados[35] = {
tx_id:35,
last_time: 789,
best_lap: 32234,
best_time: 335343,
diff: 8,
laps: [{lap:1,time:1234},{lap:2,time:32323},{lap:3,time:3242332},{lap:4,time:343232}]
}
This data in rendered to View's using map function (not a FlatList).
My problem now is that I need to order this before printing on screen.
Now, with this code, data are printed using tx_id as order, since it's the key for main array. Is there a way to order this array using number of elements in laps property and the second option to sort, use last_time property of element?
In this case, the last tx of my example (35) would be the first in the list because it has one lap more than other elements. The second item would be 34 (because of last_time). And the third would be tx 33.
Is there any way to to this in JavaScript, or I need to create a custom functions and check every item in recursive way?!
Tks #crackhead420
While waiting for reply to this question, I just found what you said.... :)
This is my final teste/solution that worked:
var t_teste = this.state.teste;
t_teste[33] = {tx_id: 33, last_time:998,best_lap:2,best_time:123,diff:0,laps:[{lap:1,time:123},{lap:2,time:456}]};
t_teste[34] = {tx_id: 34, last_time:123,best_lap:2,best_time:123,diff:0,laps:[{lap:1,time:123},{lap:2,time:456}]};
t_teste[35] = {tx_id: 35, last_time:456,best_lap:2,best_time:123,diff:0,laps:[{lap:1,time:123},{lap:2,time:456},{lap:3,time:423}]};
t_teste[36] = {tx_id: 36, last_time:789,best_lap:2,best_time:123,diff:0,laps:[{lap:1,time:123},{lap:2,time:456}]};
console.log('Teste original: ',JSON.stringify(t_teste));
var saida = t_teste.sort(function(a, b) {
if (a.laps.length > b.laps.length) {
return -1;
}
if (a.laps.length < b.laps.length) {
return 1;
}
// In this case, the laps are equal....so let's check last_time
if (a.last_time < b.last_time) {
return -1; // fastest lap (less time) first!
}
if (a.last_time > b.last_time) {
return 1;
}
// Return the same
return 0;
});
console.log('Teste novo: ',JSON.stringify(saida));
Using some simple helper functions, this is definitely possible:
const data = [{tx_id:33,last_time:456,best_lap:3455,best_time:32432,diff:32,laps:[{lap:1,time:1234},{lap:2,time:32323},{lap:3,time:3242332}]},{tx_id:34,last_time:123,best_lap:32234,best_time:335343,diff:10,laps:[{lap:1,time:1234},{lap:2,time:32323},{lap:3,time:3242332}]},{tx_id:35,last_time:789,best_lap:32234,best_time:335343,diff:8,laps:[{lap:1,time:1234},{lap:2,time:32323},{lap:3,time:3242332},{lap:4,time:343232}]}]
const sortBy = fn => (a, b) => -(fn(a) < fn(b)) || +(fn(a) > fn(b))
const sortByLapsLength = sortBy(o => o.laps.length)
const sortByLastTime = sortBy(o => o.last_time)
const sortFn = (a, b) => -sortByLapsLength(a, b) || sortByLastTime(a, b)
data.sort(sortFn)
// show new order of `tx_id`s
console.log(data.map(o => o.tx_id))
sortBy() (more explanation at the link) accepts a function that selects a value as the sorting criteria of a given object. This value must be a string or a number. sortBy() then returns a function that, given two objects, will sort them in ascending order when passed to Array.prototype.sort(). sortFn() uses two of these functions with a logical OR || operator to employ short-circuiting behavior and sort first by laps.length (in descending order, thus the negation -), and then by last_time if two objects' laps.length are equal.
Its possible to sort an object array by theire values:
dados.sort(function(a, b) {
return a.last_time - b.last_time;
});

Categories

Resources