Yanayaya
Yanayaya

Reputation: 2184

Is there anyway I can reduce my jQuery code by using a loop?

I am working on a sort of route planner and I'm not happy with the amount of repetitive code I've written especially with the calculations which are a real "rinse and repeat" affair. I was wondering if anyone could suggest (giving an example) a way for me to perhaps reduce this code?

At the moment I use $.each to grab values from input fields and I store them in an object. After this point, I access each defined object individually to perform calculations and work. I think I have perhaps overengineered this a bit!

Let me start by showing you the simple HTML that houses the input fields from which I gather my data.

<div id="plot1" class="plotrow">
    <div class="lat">
        <input id="plot1_lat" />
    </div>
    <div class="lon">
        <input id="plot1_long" />
    </div>
</div>

<div id="plot2" class="plotrow">
    <div class="lat">
        <input id="plot2_lat" />
    </div>
    <div class="lon">
        <input id="plot2_long" />
    </div>
</div>

...

Ok, at this stage I move to jQuery to grab any values (which will be latitude and longitude coordinates). I store this information in an object.

//Object is defined
var obj = {};

//Values are passed in
$('.plotrow').each(function () {
    obj[this.id] = {
        lat: $(this).find('.lat input').val(),
        lon: $(this).find('.lon input').val()
    };
});

At this stage I need to start doing work to my gathered information. Here I pass the values to a function that converts them to radians.

plot1LatRad = deg2rad(obj.plot1.lat);
plot1LonRad = deg2rad(obj.plot1.lon);
plot2LatRad = deg2rad(obj.plot2.lat);
plot2LonRad = deg2rad(obj.plot2.lon);
plot3LatRad = deg2rad(obj.plot3.lat);
plot3LonRad = deg2rad(obj.plot3.lon);

As you can see I am access each plot value individually. Here is what happens next, I work out differences between locations.

//Location A
var AtoBLat = plot2LatRad - plot1LatRad;
var AtoBLon = plot2LonRad - plot1LonRad;
AtoBSum = Math.pow(Math.sin(AtoBLat / 2), 2) + Math.cos(plot1LatRad) * Math.cos(plot2LatRad) * Math.pow(Math.sin(AtoBLon / 2), 2);
AtoBSqrt = 2 * Math.atan2(Math.sqrt(AtoBSum), Math.sqrt(1 - AtoBSum));
AtoBMiles = AtoBSqrt * Rm;
AtoBRound = round(AtoBMiles);
miles1 = AtoBRound * 0.86898;

//Location B
var BtoCLat = plot3LatRad - plot2LatRad;
var BtoCLon = plot3LonRad - plot2LonRad;
BtoCSum = Math.pow(Math.sin(BtoCLat / 2), 2) + Math.cos(plot2LatRad) * Math.cos(plot3LatRad) * Math.pow(Math.sin(BtoCLon / 2), 2);
BtoCSqrt = 2 * Math.atan2(Math.sqrt(BtoCSum), Math.sqrt(1 - BtoCSum));
BtoCMiles = BtoCSqrt * Rm;
BtoCRound = round(BtoCMiles);
miles2 = BtoCRound * 0.86898;

As you can see it's all getting very repetitive and quite bloated. Can I do this work in a loop? Can anyone suggest an approach that would help? For the sake of brevity I've only shown a couple of points but this application has as many as 10 areas you can plot a route too and so the above code will become quite big.

Upvotes: 0

Views: 66

Answers (2)

user1636522
user1636522

Reputation:

There is an error because I don't know what Rm is :

$("form").append(
  template("plot1"),
  template("plot2"),
  "<input type=\"submit\">"
).on("submit", function (ev) {
  ev.preventDefault();
  calculateMiles(
    this.elements.plot1_lat.value,
    this.elements.plot1_long.value,
    this.elements.plot2_lat.value,
    this.elements.plot2_long.value
  );
});

function template (id) {
  return ""
  + "<div id=\"" + id + "\" class=\"plotrow\">"
  +   "<div class=\"lat\">"
  +     id + " lat <input name=\"" + id + "_lat\" />"
  +   "</div>"
  +   "<div class=\"lon\">"
  +     id + " lng <input name=\"" + id + "_long\" />"
  +   "</div>"
  + "</div>";
}

function calculateMiles (plot1LatRad, plot1LonRad, plot2LatRad, plot2LonRad) {
  var AtoBLat = plot2LatRad - plot1LatRad;
  var AtoBLon = plot2LonRad - plot1LonRad;
  var AtoBSum = Math.pow(Math.sin(AtoBLat / 2), 2) + Math.cos(plot1LatRad) * Math.cos(plot2LatRad) * Math.pow(Math.sin(AtoBLon / 2), 2);
  var AtoBSqrt = 2 * Math.atan2(Math.sqrt(AtoBSum), Math.sqrt(1 - AtoBSum));
  var AtoBMiles = AtoBSqrt * Rm;
  var AtoBRound = round(AtoBMiles);
  return AtoBRound * 0.86898;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form></form>

Upvotes: 0

M0nst3R
M0nst3R

Reputation: 5283

You are correct in the fact that the code can explode if you have multiple plots, but that's why functions and loops exist. Below, I am suggesting a solution with a function locationDiff that takes two plot configurations and spits out the distance between them as you want, a loop is later introduced to cycle through the configurations object and store the results in a final array.

// does the distance calculation between two plot configurations
function locationDiff(plot1, plot2) {
    let AtoBLat = deg2rad(plot2.lat) - deg2rad(plot1.lat);
    let AtoBLon = deg2rad(plot2.lon) - deg2rad(plot1.lon);
    let AtoBSum = Math.pow(Math.sin(AtoBLat / 2), 2) + Math.cos(deg2rad(plot1.lat)) * Math.cos(deg2rad(plot2.lat)) * Math.pow(Math.sin(AtoBLon / 2), 2);
    return (round((2 * Math.atan2(Math.sqrt(AtoBSum), Math.sqrt(1 - AtoBSum))) * Rm) * 0.86898);
}

// stores the results of calling `locationDiff` on the plots
let diffs = [];
// captures the keys of the different plot configurations
let objKeys = Object.keys(obj);
// loops through the keys to calculate and store the distances
objKeys.forEach((key, index) => {
    // the next key in the keys array
    let nextKey = objKeys[index + 1];
    // if not at the end of the array yet, push the result in the final array
    if (nextKey) {
        diffs.push(locationDiff(obj[key], obj[nextKey]));
    }
});

Hope this helps.

Upvotes: 1

Related Questions