15

I'm looking for a simple way of looking up a value, using javascript, against a number of dimensions:

eg. (I'm going to use products and product options to describe this, the data is coming from a database in a very similar format)

Colour  Size Price

Blue    S    £45

Blue    L    £98

Red     S    £65

Red     L    £31

So I then have a number of dropdowns on the page

Colour: Blue, Red

Size:   Small, Large

So - I want to know...given "Blue + Small", what the price is

I have no idea what order the dropdowns are in, or the order in which Colour and Size are pulled from the database

The data in javascript may be an array like this:

{Colour:Blue, Size:Medium, Price:48},{Colour:Blue, Size:Large, Price:82}

It's a crude example, but I can't work out an easy way to achieve this in javascript.

Paul
  • 8,821
  • 11
  • 56
  • 103
  • Why jQuery and not simple js? Just asking ... – Yoshi Jan 25 '12 at 16:22
  • simple js would work as well...:) been a long day – Paul Jan 25 '12 at 16:22
  • Why do you want to do this with jQuery? A simpler approach would be to use a multi-dimensional javascript array. – Rory McCrossan Jan 25 '12 at 16:22
  • when you pass, 1 2 3 how do you get 45?? Also what have you tried and where are you facing problem?? – Fahim Parkar Jan 25 '12 at 16:23
  • I think we need to know a lot more about the data. Do you have lookup values for all values of A, B, C that are less than X? If so, then you can use a three dimensional array: `var answer = data[1][2][3];` or `answer = data[2][2][3];`. You would, of course, have to fill this three dimensional array with the proper data. – jfriend00 Jan 25 '12 at 16:23
  • The values for A, B and C aren't linear so A might have the values 1, 45, 89...would that still work for the 3d array? – Paul Jan 25 '12 at 16:29

4 Answers4

12

You can index prices in a two dimensional map on page load (with working fiddle).

1) I put the select values in lookup-tables in case you have to preload them:

var tables = {
    Colour: ["Blue", "Red"],
    Size: ["Small", "Medium", "Large"]
};

2) Here is your price table in array form:

var array = [
    {Colour: "Blue", Size: "Small", Price: 45},
    {Colour: "Blue", Size: "Medium", Price: 48},
    {Colour: "Blue", Size: "Large", Price: 98},
    {Colour: "Red", Size: "Small", Price: 65},
    {Colour: "Red", Size: "Large", Price: 31}
];

3) Initializing selects (populating values and event 'change'):

for (var key in tables)
    if (tables.hasOwnProperty(key)) {
        selects[key] = form[key];
        selects[key].addEventListener("change", updateSpan);

        var values = tables[key];
        len = values.length;
        for (i = 0; i < len; i++) {
            var option = document.createElement('option');
            option.appendChild(document.createTextNode(values[i]));
            form[key].appendChild(option);
        }
    }

4) Indexing your price table:

len = array.length;
for (i = 0; i < len; i++) {
    var record = array[i];

    if (typeof map[record.Colour] === 'undefined')
        map[record.Colour] = {};

    map[record.Colour][record.Size] = record.Price;
}

5) Function updateSpan (on select change):

function updateSpan() {
    var Colour = selects.Colour.options[selects.Colour.selectedIndex].value;
    var Size = selects.Size.options[selects.Size.selectedIndex].value;

    if (typeof map[Colour] !== 'undefined' && typeof map[Colour][Size] !== 'undefined')
        span.textContent = map[Colour][Size];
    else
        span.textContent = "Price not defined to Colour: " + Colour + " and Size: " + Size + ".";
}

6) Debugging (hit F12 in Chrome or Firefox to open Console View).

Full indexed table:

console.log(map);

Just the price of 'Blue' & 'Small':

console.log(map['Blue']['Small']); // outputs the value: 45
Adam Bowen
  • 336
  • 3
  • 9
8

This maybe?

var data = {
  1: {
    2: {
      3: 45
    }
  },
  2: {
    2: {
      3: 98
    }
  }
};

console.log(data[1][2][3]); // 45
console.log(data[2][2][3]); // 98

// or
var A = 1, B = 2, C = 3;
console.log(data[A][B][C]); // still 45
Yoshi
  • 51,516
  • 13
  • 81
  • 100
  • The values for A, B and C aren't linear so A might have the values 1, 45, 89...would that still work for this solution? (which is what I'd considered, but may have over thought what I've written) – Paul Jan 25 '12 at 16:31
  • @Paul yes of course. I'm just building an object using object literal notation. You can use whatever you want as *key*. Maybe you can add some more of your data so we can better understand what you're trying to achieve. – Yoshi Jan 25 '12 at 16:33
  • That pretty much is my data, each record has 3 key value pairs, and I'm trying to locate the record in that table by passing in 3 key value pairs (one for each column)....I think the biggest problem I'm having is with the order of the columns....I don't necessarily know it, although could probably re-order what i'm passing in – Paul Jan 25 '12 at 16:38
  • @Paul ah, yes a random order for the columns would be a problem. Where/how do you get your data from? – Yoshi Jan 25 '12 at 16:40
  • It's coming out of a database - the best way I can think of describing is maybe selecting a Product based on colour and size - colour and size being in dropdowns. You have a list of dropdowns on the page, but you're not sure what order they're in, you just know that Colour=4 and Size=9 – Paul Jan 25 '12 at 16:47
  • @Paul I'm still having problems visualizing your input-data and the way you're trying to access it in javascript. Could you please add some of both? – Yoshi Jan 25 '12 at 17:07
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/7045/discussion-between-paul-and-yoshi) – Paul Jan 25 '12 at 17:49
7

The most common solution to this is just looping over the array, in O(N) style.

var filter = {Colour: 'blue', Size:'small'};
function matches_filter(filter, item){
    //you can also have variations on this function that
    //allow for functions, regexes or anything you like in the filter...
    for(var key in filter){
        if Object.prototype.hasOwnProperty.call(filter, key){
            if(item[key] !== filter[key]){
                return false;
            }
        }
    }
    return true;
}

var filtered_items = [];
for(var i=0; i<items.length; i++){
    var item = items[i];
    if(matches_filter(filter, item)){
        filtered_items.push(item);
    }
}

The reasoning behind the brute forcing is that if you have a data set that is large enough to require a better algorithm then there is a good chance it would be best to just send the query back to the server and have its database do the hard work for you.

For a more complete example you can check this code from the Dojo toolkit.

hugomg
  • 63,082
  • 19
  • 144
  • 230
0

Since the Color + Size is representing the price we can create a enum for color and size separately by provide the index values as follows:

const color = {
  BLUE: 0,
  RED: 1    
}
const size = {
  SMALL : 0,
  LARGE : 1
}

Value assignments:

var prices[10][2];

prices[color.BLUE][size.SMALL] = 40;
prices[color.BLUE][size.LARGE] = 80;
E V N Raja
  • 80
  • 3
  • 17