Search code examples
javascriptarrayssortingjavascript-objects

Combining parallel arrays into single object array and sort


I have a table of inputs with the headers: inside dimension, outside dimension, and quantity. A user can input up to 20 different instances. For example user inputs:

Line 1 - ID = 20, OD = 30, QTY = 6
Line 2 - ID = 10, OD = 15, QTY = 3
Line 3 - ID = 40, OD = 52, QTY = 15
etc...

Currently I am collecting all of the ID's, OD's, and QTY's in parallel arrays, so for this example:

IDarray = [20, 10, 40, etc...]
ODarray = [30, 15, 52, etc...]
QTYarray = [6, 3, 15, etc...]

I have a loop that goes through the table and ignores any blank inputs when building those arrays. All of that is working fine, but I want to be able to sort them from largest to smallest based on the outside diameter (OD). It looks like the best way to do so would be to combine the 3 parallel arrays into an array of objects that contains all three elements, but I can't seem to find guidance for building an array of objects when the input values can constantly be different. I am sure this is very basic, but I am new to coding javascript, so I am struggling with this.

I have attached a snippet of my code, but I reduced it to 3 inputs to save space.

<script>

window.onload = function() {
$("#unit1").val(default_unit);
$("#unit2").val(default_unit);
$("#unit3").val(default_unit);


function myFunction() {

var unit1 = document.getElementById("unit1").value;
var unit2 = document.getElementById("unit2").value;
var unit3 = document.getElementById("unit3").value;


var id1 = (unit1 == 1) ? document.getElementById("id1").value:document.getElementById("id1").value / 25.4;
var id2 = (unit2 == 1) ? document.getElementById("id2").value:document.getElementById("id2").value / 25.4;
var id3 = (unit3 == 1) ? document.getElementById("id3").value:document.getElementById("id3").value / 25.4;

var od1 = (unit1 == 1) ? document.getElementById("od1").value:document.getElementById("od1").value / 25.4;
var od2 = (unit2 == 1) ? document.getElementById("od2").value:document.getElementById("od2").value / 25.4;
var od3 = (unit3 == 1) ? document.getElementById("od3").value:document.getElementById("od3").value / 25.4;

var qty1 = document.getElementById("qty1").value;
var qty2 = document.getElementById("qty2").value;
var qty3 = document.getElementById("qty3").value;

Array.prototype.clean = function(deleteValue) {
    for (var i = 0; i < this.length; i++) {
        if (this[i] == deleteValue) {         
            this.splice(i, 1);
            i--;
        }
    }
    return this;
};

var id_values = [id1, id2, id3].clean("");
var od_values = [od1, od2, od3].clean("");
var qty_values = [qty1, qty2, qty3].clean("");               
alert(id_values);
alert(od_values);
alert(qty_values);


//var testObject = {
//  id_values,
//  od_values, <--sort by this
//  qty_values;
//};
}
</script>
<table class="inputstable">
    <tr>
        <th>Unit</th>
        <th>Inside Dimension</th>
        <th>Outside Dimension</th>
        <th>Quantity</th>
    </tr>
    <tr>
        <td><select class="custom_select" id="unit1">
            <option value=1>in</option>
            <option value=2>mm</option>
        </td>
        <td><input class="custom_input" type="text" id="id1"></td>
        <td><input class="custom_input" type="text" id="od1"></td>
        <td><input class="custom_input" type="number" id="qty1"></td>
    </tr>
    <tr>
        <td><select class="custom_select" id="unit2">
            <option value=1>in</option>
            <option value=2>mm</option>
        </td>
        <td><input class="custom_input" type="text" id="id2"></td>
        <td><input class="custom_input" type="text" id="od2"></td>
        <td><input class="custom_input" type="number" id="qty2"></td>
    </tr>
    <tr>
        <td><select class="custom_select" id="unit3">
            <option value=1>in</option>
            <option value=2>mm</option>
        </td>
        <td><input class="custom_input" type="text" id="id3"></td>
        <td><input class="custom_input" type="text" id="od3"></td>
        <td><input class="custom_input" type="number" id="qty3"></td>
    </tr>

<button class="special_button" onclick="myFunction()">Tester</button>

Thanks!


Solution

  • From what I understand, you end up with three arrays. These arrays will be of consistent length, with the index representing which row in the table they are. What you want is to sort the indices of all three arrays based on the values of one. This means that you need to ensure whenever a value in one array is moved, the values of all other arrays are moved from identically.

    Unfortunately, there is no way of doing this that I'm aware of with native functions. You'd have to implement the sort yourself to manage it that way.

    On the front end, It's better to have associated data structured as an an array of objects rather than a set of associated table-like arrays. That way, you can more intuitively perform sorts, searches, filters, etc. Let's go over how to do that.

    I'll use your test object:

    var testObject = {
      id_values: [20, 10, 40],
      od_values: [30, 15, 2],
      qty_values: [6, 3, 15]
    };
    

    To convert the associated arrays to an array of objects containing associated data, I'll instantiate a zero-filled array with the same number of indices as the tables. Then, we map over this blank array, creating an object from the data in each table:

    const dataArray = new Array(obj[by].length).fill(0).map((_, i) => {
      return {
        id_values: testObject.id_values[i],
        od_values: testObject.od_values[i],
        qty_values: testObject.qty_values[i]
      }
    }) .sort((a, b) => a[by] > b[by])
    

    At this point, the tables will be converted into the more JavaScript-friendly array of objects, which will look like this:

    const dataArray = [ 
      { id_values: 20, od_values: 30, qty_values: 6 },  
      { id_values: 10, od_values: 15, qty_values: 3 },  
      { id_values: 40, od_values: 2, qty_values: 15 } 
    ]
    

    Now THAT can be easily sorted using the native array sort:

    dataArray.sort((a, b) => a.od_values > b.od_values)
    

    Which will result in it being sorted!

    const sortedData = [ 
      { id_values: 40, od_values: 2, qty_values: 15 },  
      { id_values: 10, od_values: 15, qty_values: 3 },  
      { id_values: 20, od_values: 30, qty_values: 6 } 
    ]
    

    The values can the be destructured into their associated tables by reducing the array of objects:

    dataArray.reduce((acc, el) => {
      Object.keys(el).map(col => {
        acc[col].push(el[col])
      })
      return acc
    }, {
      id_values: [],
      od_values: [],
      qty_values: []
    })
    

    Resulting in the original data structure, but sorted by the selected attribute:

    const sortedArrays = { 
      id_values: [ 40, 10, 20 ],  
      od_values: [ 2, 15, 30 ],  
      qty_values: [ 15, 3, 6 ] 
    }
    

    Putting it all together:

    var testObject = {
      id_values: [20, 10, 40],
      od_values: [30, 15, 2],
      qty_values: [6, 3, 15]
    };
    
    const sortTableObj = (obj, by) => {
      const dataArray = new Array(obj[by].length).fill(0).map((_, i) => {
        return {
          id_values: testObject.id_values[i],
          od_values: testObject.od_values[i],
          qty_values: testObject.qty_values[i]
        }
      }).sort((a, b) => a[by] > b[by])
      console.log('dataArray: ', dataArray) 
    
      return dataArray.reduce((acc, el) => {
        Object.keys(el).map(col => {
          acc[col].push(el[col])
        })
        return acc
      }, {
        id_values: [],
        od_values: [],
        qty_values: []
      })
    }
    
    sortTableObj(testObject, 'od_values')
    

    Let me know if you have any questions!