Sorting a repeater by calculated field

Hello everybody, I always sort repeaters by fields in the database. I would like to know if it is possible to sort it by a calculated repeater item. I have a function that return numbers that are shown in a text item of my repeater as shown in the image below.


Those numbers are not stored in the database and they change constantly. I want to know if its possible to sort the repeater via corvid from the lowest to the highest of those numbers , Anyone have any suggestions?

It’s not clear from your explanation how and where the function is being called, but that function probably could be invoked while looping through the results of a query, adding a property to the results, re-sorting it, and then the refined results could be assigned to the repeater.

Hi :raised_hand_with_fingers_splayed:

You can go with Anthony’s answer, or you can sort the items on the query itself.

Example:

wixData.query('students').ascending("last_name", "first_name")
.find()

This will sort the result items before fetching them from the database.

Read more about ascending() and descending().

Ahmad

Hello Anthony! I will explain myself better.

ENVIROMENT: The numbers are calculated in a function called dist() then is being called inside the repater´s on item ready function, I have the latitudes and longitudes of all the properties in the database and then I get the user current location with the getCurrentGeolocation() API and with those values the dist() function does the math to get the exact distance between the user and each property. The resulting numbers (distances) are then displayed in a text element of the repeater as shown in the first image.

PROBLEM: The calculation is working as expected, the problem is that the values (distances) are not stored in the database and they are diferent to each user depending on their current location therefore the repeater has to be sorted from the nearest property to the farthest dependig on the distances that each user get .

Code for the Distances calculations

// function to calculate distance in km from the user current location to a property´s location
$w.onReady(function () {

});

function dist (lat1,lon1,lat2,lon2) {
 function rad(x) {return x * Math.PI/180;}

 let R = 6378.137;
 let dLat = rad(lat2 - lat1);
 let dLong = rad(lon2 - lon1);

 let a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(rad(lat1)) * Math.cos(rad(lat2)) * Math.sin(dLong/2) * Math.sin(dLong/2);
 let c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
 var d = R * c;
    
 return d.toFixed(3);
 
}

Code for the repearter

export function propertiesRepeater_itemReady($item, itemData, index) {
 
 // when repeater is ready get users current geolocation
        wixWindow.getCurrentGeolocation()
        .then((obj)=>{
 let latitude = obj.coords.latitude;
 let longitude = obj.coords.longitude;
        console.log("my location is: " + latitude + longitude);
 

 let lat1 = latitude //current latitudo of the user
 let lon1 =   longitude //current longitude of the user
 let lat2 = itemData.lat; //get the latitude of each property
 let lon2 =  itemData.lon; //get the longitude of each property

 let distance = dist(lat1, lon1, lat2, lon2) // call the fucntion dist to calculate the distance between the curren user and the properties 
 
        $item("#txtDistance").text =  distance + "Km away";
 
        })
        .catch( (error) => {
 let errorMsg = error;
        });
 
}

Hello Ahmad. I think wixData query nor dataset setSort() is not useful for this probem as I explained to Anthony the reference values to apply the sorting are the distances numbers but they are not stored in the database and they are different to each user for example user A could get 100m for the property at the lakes and 200m for the property at Sun Valley but at the same time user B get 500m for the lakes and 300m for Sun valley depending on each of their location.

So I want to sort the repater form the nearest to the farthest this menas that user A will get The Lakes firts then Sun Valley but user B will get Sun Valley first then The lakes. I hope that make sense!

I haven’t read the full thread but you can apply JS .sort() method on the query results.

Thanks for explaining it further.

Roughly, just to get the idea of what I had in mind, here is some code:

export function GetData(){
   wixWindow.getCurrentGeolocation()
   .then((obj)=>{
      let latitude = obj.coords.latitude;
      let longitude = obj.coords.longitude;
      wixData.query("properties")
      .find()
      .then((results) => {
         let props = results.items;
          for (var i = 0; i < props.length; i++) {
             let lat1 = latitude //current latitudo of the user
             let lon1 =   longitude //current longitude of the user
             let lat2 = props[i].lat; //property latitude
             let lon2 =  props[i].lon; //property longitude
             let distance = dist(lat1, lon1, lat2, lon2);
             // new property in object array to store the distance
             props[i].distance = distance;
         }
          // now sort
         props.sort((a, b) => (a.distance > b.distance) ? 1 : -1);
         console.log(props);
          // lastly, connect it to the repeater
         $w("#propertiesRepeater").data = props;
      })    
   }) 
}

As J. D. said, you can map the results to specific key, then sort the mapped result and use a for loop to push the sorted elements into a new array.

Thanks a lot Anthony I just tried your example and it works perfectly! I used a button called “Near me” to set the query and the sorting.
Now I have to figure out how to set up a load more button to load more properties with the sorting parameter, I used an example article by wix on how to achive that for so far no luck. I´ll put my code in case you can help me with the Load more button too, anyway you already helped me a lot , thanks again!

This is the code you gave me I just limited to (3) and add a few lines of code at the bottom to check if the query has more items.

export function btnNear_click(event) {
   wixWindow.getCurrentGeolocation()
   .then((obj)=>{
 let latitude = obj.coords.latitude;
 let longitude = obj.coords.longitude;

      sortResults =  wixData.query("Properties")
     .limit(2)
      .find()
      .then((results) => {
 let props = results.items;
 for (var i = 0; i < props.length; i++) {
 let lat1 = latitude //current latitudo of the user
 let lon1 =   longitude //current longitude of the user
 let lat2 = props[i].lat; //property latitude
 let lon2 =  props[i].lon; //property longitude
 let distance = dist(lat1, lon1, lat2, lon2);
 // new property in object array to store the distance
             props[i].distance = distance;
         }
 // now sort
         props.sort((a, b) => (a.distance > b.distance) ? 1 : -1);
         console.log(props);
 // lastly, connect it to the repeater
         $w("#propertiesRepeater").data = props;

 if(sortResults.hasNext()){
 // load more button was set as collapsed on load
               $w('#btnMore').expand();
             }
 
      })
   }) 
}


This is the Code to load more properties, not working yet.

export function btnMore_click(event) {
   sortResults = sortResults.next();
 let data =  $w("#propertiesRepeater").data 
   $w("#propertiesRepeater").data = data.concat(sortResults.items);
 if(!sortResults.hasNext()){
    $w('#btnMore').collapse();
  }
}

Hello J.D and Ahmad!
anthony´s code is working but I´m going to try JS.sort() method too, it is always good to know different ways to achieve a problem, in fact before this post I had already read the link of the sort() method but i didn´t know to apply it to a repeater with values ​​that are not in the database. if you can guide me or give a more specific article on how to achive these it would be great!

Anthony’s code includes the .sort() method.

Oh I see J.D. thanks for clarifying it, I appreciate it!
Now I just have to make the code for the Load More properties with the same sorting works. :sweat_smile:

@tony-brunsman Hello again anthony, I hope you are doing well!
The code that you gave me to sort the repeater by the calculated distance is working just fine. Now I want to know if there is a way to limit the sorting results to only show those thats are only in some range. For example in the photo on the top if I limit the sorting to “6” only Oakhill will show up because the distance is 5.4

I know that with wixdata.query I can use the limit() function to achieve this but in this case I´m a little lost since the sorting values are calculated and not in the Database

Guys, I don’t know if I should make a new thread or just post here but it’s related to this one as I’m using the code examples from here.

Can you help me? I’m trying to do the exact same thing Blink is doing except I cannot get the code to show me the distance (it seems to be arranging the objects(properties) in the repeater in the correct order of nearest distance but it wont display the actual distance. the console just gives me a NaN error.

Below is my code:

// API Reference: https://www.wix.com/velo/reference/api-overview/introduction
// “Hello, World!” Example: https://learn-code.wix.com/en/article/1-hello-world

import wixWindow from 'wix-window';
import wixData from "wix-data";

$w.onReady(function () {

function propertiesRepeater_itemReady($item, itemData, index) {
 
 // when repeater is ready get users current geolocation
        wixWindow.getCurrentGeolocation()
        .then((obj)=>{
 let latitude = "obj.coords.latitude";
 let longitude = "obj.coords.longitude";
        console.log("my location is: " + latitude + longitude);

 wixData.query("Properties")
 let lat1 = latitude //current latitudo of the user
 let lon1 = longitude //current longitude of the user
 let lat2 = $w('#textLat'); //get the latitude of each property
 let lon2 = $w('#textLong'); //get the longitude of each property

 let distance = dist(lat1, lon1, lat2, lon2) // call the fucntion dist to calculate the distance between the curren user and the properties 
    $item("#txtDistance").text =  distance;
 
        })
        .catch( (error) => {
 let errorMsg = error;
        });
 
}
});

function dist (lat1,lon1,lat2,lon2) {
 function rad(x) {return x * Math.PI/180;}

 let R = 6378.137;
 let dLat = rad(lat2 - lat1);
 let dLong = rad(lon2 - lon1);

 let a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(rad(lat1)) * Math.cos(rad(lat2)) * Math.sin(dLong/2) * Math.sin(dLong/2);
 let c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
 var d = R * c;
 
 return d.toFixed(3);
 
}





export function button7_click(event) {

   wixWindow.getCurrentGeolocation()
   .then((obj)=>{
 let latitude = obj.coords.latitude;
 let longitude = obj.coords.longitude;
 let speed = obj.coords.speed;                   // null
         console.log("Your latitude is:" + latitude);
         console.log("Your longitude is:" + longitude);
      wixData.query("Properties")
      .find()
      .then((results) => {
 let props = results.items;
 for (var i = 0; i < props.length; i++) {
 let lat1 = latitude //current latitudo of the user
 let lon1 = longitude //current longitude of the user
 let lat2 = props[i].lat; //property latitude
 let lon2 = props[i].lon; //property longitude
 let distance = dist(lat1, lon1, lat2, lon2);
 // new property in object array to store the distance
             props[i].distance = distance;
 
         }
 // now sort
         props.sort((a, b) => (a.distance > b.distance) ? 1 : -1);
 
         console.log(props);
 // lastly, connect it to the repeater
         $w("#propertiesRepeater").data = props;
      })    
   }) 
}




Basically, console is showing me my longitude and latitude correctly. and when I click the button the console shows me the property long and lat which I have put in a database but console gives me a NaN (not a number) error for the distance.

I have tried to put the long and lat database as a number type and text field but it’s the same thing either ones dont work.

Can someone help me?

Both are set to either number or text field but i still get the NaN error in the console.

TL:DR

Need to know why distance is not calculated (giving NaN - not a number error).

I have also correctly named the textDistance box but the equation comes up wrong so it doesn’t change anyway.

Any help will be greatly greatly appreciated!

Seems you are mixing types. In your DB collection, the props lat field is string and the props lon field is number .

Hi, thank you for your help. I did change both to either number or string but it’s still not working.

@shrimpbgnet If the fields are string , then you need to convert them to number before using them in a calculation. The result itself should be kept as number so that you can sort correctly.

To set the screen text field, you will need to convert to string. This is one way:

props[i].distance = distance;    // we want number here
$item("#txtDistance").text =  distance + ''; // to display, you want string

Of course, it could be that your calculation is wrong and you getting a result that is invalid - that is, NaN.

@yisrael-wix

Yes I think its something wrong with my calculation. I did do the adjustments you said into my code but I’m still getting a NaN (although weirdly the objects in the repeater appear in the correct order by distance after executing the button. I just cant get the distance to show or calculate properly.

Also I’m not sure about these lines in my code


 wixData.query("Properties")
 let lat1 = latitude //current latitudo of the user
 let lon1 = longitude //current longitude of the user
 let lat2 = itemData.lat; //get the latitude of each property
 let lon2 = itemData.lon;//get the longitude of each property

the last 2 lines. Maybe I am getting the wrong info from here?

Blink had 2 versions of this:

his first version had:

 let lat1 = latitude //current latitudo of the user
 let lon1 =   longitude //current longitude of the user
 let lat2 = itemData.lat; //get the latitude of each property
 let lon2 =  itemData.lon; //get the longitude of each property

and his second version had:

 let lat1 = latitude //current latitudo of the user
 let lon1 =   longitude //current longitude of the user
 let lat2 = props[i].lat; //property latitude
 let lon2 =  props[i].lon; //property longitude

I did try the first one and run into the same NaN, although with the second one it tells me that props is undefined. I tried propslat and propslon from my database but those dont seem to work.

Also I tried this version of code but it could be wrong altogether. Most likely getting the info from $w(‘#textLat’) and $w(‘#textLong’) is incorrect but I’ve tried the other ways mentioned here too.

 let lat1 = latitude //current latitudo of the user
 let lon1 = longitude //current longitude of the user
 let lat2 = $w('#textLat'); //get the latitude of each property
 let lon2 = $w('#textLong'); //get the longitude of each property

Ive been trying to figure this out for days. I’m really thankful for your help even if we dont resolve it.

@shrimpbgnet Try the following code:

import wixWindow from 'wix-window';
import wixData from "wix-data";

$w.onReady(function () {
        setUpRepeater();
});

function dist(lat1, lon1, lat2, lon2) {
 function rad(x) { return x * Math.PI / 180; }

 let R = 6378.137;
 let dLat = rad(lat2 - lat1);
 let dLong = rad(lon2 - lon1);

 let a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(rad(lat1)) * Math.cos(rad(lat2)) * Math.sin(dLong / 2) * Math.sin(dLong / 2);
 let c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
 var d = R * c;

 return d.toFixed(3);

}

function setUpRepeater() {
    $w('#propertiesRepeater').onItemReady(($item, itemData, index) => {
 // when repeater is ready get users current geolocation
        wixWindow.getCurrentGeolocation()
            .then((obj) => {
 let latitude = "obj.coords.latitude";
 let longitude = "obj.coords.longitude";
                console.log("my location is: " + latitude + longitude);

                wixData.query("Properties")
 let lat1 = latitude //current latitudo of the user
 let lon1 = longitude //current longitude of the user
 let lat2 = $item('#textLat'); //get the latitude of each property
 let lon2 = $item('#textLong'); //get the longitude of each property

 let distance = dist(lat1, lon1, lat2, lon2) // call the fucntion dist to calculate the distance between the curren user and the properties 
                $item("#txtDistance").text = distance;

            })
            .catch((error) => {
 let errorMsg = error;
            });
    })
}

export function button7_click(event) {

    wixWindow.getCurrentGeolocation()
        .then((obj) => {
 let latitude = obj.coords.latitude;
 let longitude = obj.coords.longitude;
 let speed = obj.coords.speed; // null
            console.log("Your latitude is:" + latitude);
            console.log("Your longitude is:" + longitude);
            wixData.query("Properties")
                .find()
                .then((results) => {
 let props = results.items;
 for (var i = 0; i < props.length; i++) {
 let lat1 = latitude //current latitudo of the user
 let lon1 = longitude //current longitude of the user
 let lat2 = props[i].lat; //property latitude
 let lon2 = props[i].lon; //property longitude
 let distance = dist(lat1, lon1, lat2, lon2);
 // new property in object array to store the distance
                        props[i].distance = distance;

                    }
 // now sort
                    props.sort((a, b) => (a.distance > b.distance) ? 1 : -1);

                    console.log(props);
 // lastly, connect it to the repeater
                    $w("#propertiesRepeater").data = props;
                })
        })
}

@blinkco16 Thank you so much for your help. We are making progress as the textbox of distance does change now but it’s still throwing up the NaN code.

I tried checking the live database and the property long and are number.

I’m not sure where I’m screwing up. The console is showing the correct long and lat of the properties in the database and it’s showing the correct current user location in the console but it’s just not showing the calculation correctly for the distance equation