Search code examples
javascriptarraysobjectdynamiceval

How to create if conditional statement from string within javascript for loop without using eval()


I'm trying to create an advance search function to use on a multi-dimensional array of product objects. On the form, the user will have the option of narrowing down product results based on data in several different attributes (e.g. "'price' < 20 && 'country' == 'France'", "region == 'New York' && 'vintage' == '2016' && 'qty > 20"). The data, within variable data is formatted as follows:

    0:
        name: "Kedem Cream Red Concord"
        appellation: "New York"
        country: "United States"
        price: "10.99"
        primarygrape: "Concord"
        qty: "1"

        region: "New York"
        regprice: "10.99"
        sku: "230"
        vintage: "NV"
    1:
        name: "Kendall Jackson Vintner's Chardonnay"

        appellation: ""

        country: "United States"

        price: "14.99"

        primarygrape: "Chardonnay"
        qty: "35"

        region: "California"

        regprice: "18.99"
        sku: "345"

        vintage: "2016"
... continuing on over 10,000 product records

I figured I could create a string based on the user form input fairly easily that sums up what they're searching for. However, I need a safe way to evaluate that string within my function that returns the filtered results in resultsArray

I was able to get it to work with the eval() function, but I know that's not safe or efficient. I also tried fooling around with putting the conditions into an array and trying to incorporate that into the for loop, but I couldn't figure out how to make it work. The tricky part is, the conditions could be any number of things involving the following product attributes: appellation, country, price, primarygrape, qty, region, vintage. And different attributes use different operators (e.g. country, region, and others use '==' while qty and price use '>' or '<').

This code works, but uses the taboo eval() function:

var conditional = "data[i].price < 20 && data[i].country == 'France' && data[i].vintage == '2016'";

var resultArray = [];
console.log(data.length);
for (var i = 0; i < data.length; i++)
{
    if (eval(conditional))
        {
            resultArray.push(data[i]);
        }
    }
}

This doesn't work, as it results in a Uncaught ReferenceError: data is not defined error:

var conditional = "return data[i].price < 20 && data[i].country == 'France' && data[i].vintage == '2016'";

var resultArray = [];
console.log(data.length);
for (var i = 0; i < data.length; i++)
{
    if (new Function(conditional)())
        {
            resultArray.push(data[i]);
        }
    }
}

Any idea how to dynamically create that if conditional statement so that it recognizes the data variable? Or, is there a better solution?


Solution

  • Javascript Arrays have a function called filter which can be used to do things like you want to achieve.

    For example you have an array with numbers:

    var arr = [1,3,6,2,6]

    And your plan is to filter all even numbers.You can use array.filter(). you need to pass an anonymous function into it. This function gets called once every entry in which you have access to the current element. You need to return a boolean in this function - true means that the element will be in the new, filtered array

    Now the completed example to filter all even numbers:

    arr.filter(function(element) {return element%2 == 0})

    I hope that's enough for you to continue. :-) In the next snippet I made a small example with your data array

     var your_data_array = [
     { 
           name: "Kendall Jackson Vintner's Chardonnay",
            appellation: "",
            country: "United States",
            price: "14.99",
            primarygrape: "Chardonnay",
            qty: "35",
            region: "California",
            regprice: "18.99",
            sku: "345",
            vintage: "2016" },
            { 
           name: "Kendall Jackson Vintner's Chardonnay",
            appellation: "",
            country: "United States",
            price: "14.99",
            primarygrape: "Chardonnay",
            qty: "35",
            region: "California",
            regprice: "18.99",
            sku: "345",
            vintage: "2016" },
            { 
           name: "Kendall Jackson Vintner's Chardonnay",
            appellation: "",
            country: "United States",
            price: "14.99",
            primarygrape: "Chardonnay",
            qty: "35",
            region: "California",
            regprice: "18.99",
            sku: "345",
            vintage: "2016" }
     ]
     
     var filtered_array = your_data_array.filter(function(dataElement) {
       return dataElement.country == "United States";
     })
     
     console.log(filtered_array)