2

I have a Javascript array:

var peoples = [  
{'people':{'sex':'male', 'diet':'vegetarian','favPlaces':['place1','place2']}},  
{'people':{'sex':'female', 'diet':'flexitarian','favPlaces':['place2','place3']}},  
{'people':{'sex':'male', 'diet':'flexitarian', 'favPlaces':['place4','place1']}}  
];

If I select {'sex':['female','male']} && {'diet':['flexitarian']} the output should be ['place2', 'place3', 'place4', 'place1'].

The problem to my code is if I only select 'sex' it will not display result but if I select both a 'sex' and a 'diet' it will display results. I cant figure out how to make my code dynamic, so that the code will filter dynamically when I select a sex or diet or sex and diet and they will show results.

This is my code:

 var filterObj = {'sex': ['female', 'male'], 'diet': ['flexitarian']};

    for(var p in peoples){  
       for(var s in filterObj.sex){  
          for(var d in filterObj.diet){
          if((filterObj.sex[s] == peoples[p].people.sex) && (filterObj.diet[d] == peoples[p].people.diet)){  
            console.log(peoples[p].people.favPlaces);
           }
       }  
    }  

This is the HTML:

<ul id="sex">
<li><label><input type="checkbox" value="male">Male</label></li>
<li><label><input type="checkbox" value="female">Female</label></li>
</ul>
<ul id="diet">
<li><label><input type="checkbox" value="none">None</label></li>
<li><label><input type="checkbox" value="flexitarian">Flexitarian</label></li>
<li><label><input type="checkbox" value="vegetarian">Vegetarian</label></li>
</ul>
Gen
  • 43
  • 1
  • 5
  • You don't need `new Object()` there, it can be safely removed. Only keep the contents. – SeinopSys Apr 04 '15 at 11:37
  • Please have a look at this [SO answer](http://stackoverflow.com/questions/28354252/trouble-with-math-in-javascript-when-elements-clicked/28355096#28355096). I think the Underscore `filter` method would also work for you. – AWolf Apr 04 '15 at 11:49
  • @AWolf, i will check that thanks – Gen Apr 04 '15 at 11:57
  • I think your code is working but three for loops are hard to understand. Please have a look at this [jsFiddle](http://jsfiddle.net/awolf2904/rdfyp5c9/). – AWolf Apr 04 '15 at 12:00
  • My code works if i select both 'sex' and 'diet' but if i only select 'diet' it will not show results. – Gen Apr 04 '15 at 12:07

2 Answers2

1

You can use Array.indexOf and inverting the conditions such as... checking if individual data can be found inside the condition but also ignore the filter if there is nothing in it (by checking its length) as shown below...

var matchingSet = peoples.filter(function(p) {
    return (filterObj.sex.length === 0 || filterObj.sex.indexOf(p.people.sex) !== -1)     //peoples' sex must exists in the filter.sex or ignore it if filter not specified
    && (filterObj.diet.length === 0 || filterObj.diet.indexOf(p.people.diet) !== -1);     //and people's diet must exists in the filter.diet or ignore it if filter not specified
});

Afterward, use Array.map to pull out just the favPlaces out of the matching set.

var matchingPlaces = matchingSet.map(function(p) { return p.people.favPlaces; });

And finally, use Underscore (easier than making your own since I think there is no native JavaScript Array methods for this yet... to flatten out, extract unique values from the favPlaces arrays and sort it (as extra bonus):

var flatten = _.chain(matchingPlaces)
    .flatten()
    .uniq()
    .sortBy(function(p) { return p; })
    .value();

This will yield you the result that you are looking for.

["place1","place2","place3","place4"]

Full example can be found at: http://jsfiddle.net/rdfyp5c9/2/

Note that in the example I removed the diet filter elements:

var filterObj = {
    'sex' : ['male'],
    'diet': []
};

and it is correctly returning

["place1","place2","place4"]

for result.

You can definitely refine the filter at the matchingSet assignment line to check for say if filterObj.sex is not truthy and only perform the length check if it is array, etc. but to keep thing simple... this particular solution assume that if you don't want diet in filter, it is required to just pass empty array as diet...

Jimmy Chandra
  • 6,333
  • 4
  • 24
  • 37
0

Using the open source project jinqJs it should be easy

var peoples = [  
{'people':{'sex':'male', 'diet':'vegetarian','favPlaces':['place1','place2']}},  
{'people':{'sex':'female', 'diet':'flexitarian','favPlaces':['place2','place3']}},  
{'people':{'sex':'male', 'diet':'flexitarian', 'favPlaces':['place4','place1']}}  
];

var result = jinqJs().from(peoples).where(function(row){return (row.people.sex === 'female' || row.people.diet === 'flexitarian');}).select(function(row){return row.people.favPlaces;});

document.body.innerHTML = '<pre>' + JSON.stringify(result, null, 4) + '</pre><br><br>';
<script src="https://rawgit.com/fordth/jinqJs/master/jinqjs.js"></script>
NYTom
  • 494
  • 2
  • 14