1

I'm working with an extremely large data cube that I need to manipulate for data visualization purposes. I have a multidimensional array contains arrays of every possible combination of objects, with the final object of each array being an associated value.

I'm using multiple inputs (each with id's enumerated based off of the value's index in data) to allow users to filter the results by each object within the array, except for the last object that is displayed as "total available". From those inputs I'm aggregating selections into a variable so that the index of the selections corresponds to the index position of the associated value in data.

var selections = [];
var data = [[val0, val1, val2, 5121231], [val0, val1, val2, 2242], [val0, val1, val2, 72356], [val0, val1, val2, 24122], [val0, val1, val2, 75632]];

$('#fields').change(function(){
    for (var i = 0; i < data[0].length; i ++){
        selections[i] = $('select#field' + i).val();
    }
});

This will create a multidimensional array that looks like this:

selections = [[data[0][0], data[1][0], data[2][0], data[3][0], data[4][0]], [data[0][1], data[1][1], data[2][1], data[3][1], data[4][1]], [data[0][2], data[1][2], data[2][2], data[3][2], data[4][2]], [data[0][3], data[1][3], data[2][3], data[3][3], data[4][3]]]

What I'm attempting to do is filter data such that for ALL selections made, if data[x][i] === any value in selections[i], data[x] is populated into a new array.

Any thoughts?

*****UPDATE*****

A HUGE thanks to Dave for his help! We were able to set up dependent dropdown filters that manipulates a keyless multidimensional array (data cube). If you're working with large data sets in-browser, take a look at the solution. Here's what he/we came up with:

https://jsfiddle.net/JSnoobDC16/yteu6cbb/28/

DGaffneyDC
  • 152
  • 1
  • 2
  • 12

1 Answers1

0

Since you're using jQuery, this can easily be solved using $.grep() combined with Array.some().

var newArray = $.grep(data, function(item) {
  return selections.some(function(selectedValue, i) {
    return selectedValue.indexOf(item[i]) >= 0;
  });
});

There's a full snippet below and an updated fiddle here: https://jsfiddle.net/gh8fzxLh/

//function to initialize the materialize dropdowns
function pageReady() {
  $(document).ready(function() {
    $('select').material_select();
  });
}

//function to get the sum of object elements
function sum( obj ) {
  var sums = 0;
  for( var el in obj ) { 
    if( obj.hasOwnProperty( el ) ) {
      sums += parseFloat( obj[el] );
    }
  }
  return sums;
}

//function to return array of all values at a certain index positon
function getFields(matrix, index){
  var fields = [];
  for (var i = 1; i<matrix.length; i++ ){
    fields.push(matrix[i][index]);
  }
  return fields;
}

//function to return only unique values from array
function unique(arr) {
  var hash = {}, uniqueArr = [];
  for ( var i = 0, l = arr.length; i < l; ++i ) {
    if ( !hash.hasOwnProperty(arr[i]) ) { 
      hash[ arr[i] ] = true;
      uniqueArr.push(arr[i]);
    }
  }
  return uniqueArr;
}

//importing data set
var data = [
  ["state", "gender", "martial staus", "ethnicity"],
  ["GA", "null", "null", "Caucasian", 5086317],
  ["FL", "", "null", "null", 4338099],
  ["IN", "M", "null", "African-American", 72238],
  ["GA", "", "married", "Caucasian", 390578],
  ["MO", "null", "null", "Caucasian", 4165871],
  ["MO", "", "married", "Caucasian", 344501],
  ["NY", "null", "null", "African-American", 1204504],     ["AR", "M", "single", "Caucasian", 164311],
  ["CO", "null", "married", "null", 551192],
  ["OH", "null", "married", "Caucasian", 1017924],
  ["LA", "M", "null", "East Asian", 3229],
  ["AZ", "F", "single", "Uncoded", 21302],
  ["AR", "", "married", "Middle Eastern", 187]
];

var selections = []; //variable to hold all selected values
var valCombos = data[0].length; //establishing number of dropdown selection objects

/* Adding multiselection dropboxes (and soon to be other data inputs)
for each field. */
for (var i = 0; i < valCombos; i ++){
  var options = unique(getFields(data, i)).sort();
  $('#selectBar').append('<div class="row section params" id="fieldRow' + i + '"><div class="col s12 input-field" id="fieldparams"><select class="fields" id="field' + i + '" multiple><option value="" disabled selected>Select Values</option></select><label>' + data[0][i] + '</label></div></div>');
  $.each(options, function (index, value) {
    $('#field' + i).append('<option value="' + value + '">' + value + '</option>');
  });
}

pageReady();

/*Updating "selections" array as user makes selections. 
"Selections" will be used to filter the cube for visualizations. */
$('.fields').change(function(){
  for (var i = 0; i < valCombos; i ++) {
    selections[i] = $('select#field' + i).val();
  }
  console.log(selections);

  var selectedData = getSelectedData(data, selections);
  
  console.log(selectedData);
  $("#count").text(sumData(selectedData));

});

function getSelectedData(data, selections) {
  return $.grep(data, function(item) {
    return selections.some(function(value, i) {
      return value.indexOf(item[i]) >= 0;
    });
  });
}

function sumData(selectedData) {
  return selectedData.reduce(function(prev, current) {
    return prev + current[current.length - 1];  
  }, 0);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/css/materialize.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/js/materialize.min.js"></script>
<div id="count">
  Sum of lastIndexOf values displays here
</div>
<div id="selectBar">
</div>
Dave
  • 10,748
  • 3
  • 43
  • 54
  • I dug into the JQuery documentation around $.grep(), but I don't think I'm implementing your suggested snippet correctly. The problem mainly stems from the fact that the various arrays in "selections" can have equal values. So, if I'm returning any item in data that matches some value in selections without specifying *which* item matches from which value (ex: `data[i][x] == selections[x][y] !== selections[y][y]`), then I'll get a lot of false positives. – DGaffneyDC Jun 08 '16 at 14:13
  • Maybe a fiddle is the best way to share, since there are a number of dependencies I'm working with. https://jsfiddle.net/JSnoobDC16/yteu6cbb/12/ – DGaffneyDC Jun 08 '16 at 17:21
  • Ah, I think I get it. My syntax was slightly off and I had a small bug in it. See the updated answer and updated fiddle. Is this what you are looking for? – Dave Jun 09 '16 at 17:32
  • So close! I need dependency between the filters. So essentially, if you make a selection in the "State" filter, then any selection you make thereafter will satisfy both the "State" selection AND the new selection. For instance: "State = AR" returns two arrays. If I were then to select "Gender = M", selections would now only contain the array where "State = AR && Gender = M". – DGaffneyDC Jun 09 '16 at 17:57
  • I got it! Solution in the updated post. Thanks so much for your help, Dave! – DGaffneyDC Jun 09 '16 at 18:29
  • I'm glad I could help. I should mention that if you only want the sum, this can be simplified so you aren't iterating the array twice. Originally I thought you actually needed the array output. You could do an `$.each()` instead of a `$.grep()` – Dave Jun 10 '16 at 04:34