KnockoutJS Array Filtering

by mrrodd

HTML

<div class='liveExample'> 
   <p style="color:red">How do I make this list unique?</p>
    <p>
    <select data-bind="options: uniqueCountries, selectedOptions: selectedCountries" size="5" multiple="true" style="width:150px"></select>
  </p>
  <p style="color:red">And how do I filter the records below based on the selected items above? (multiple select)</p>
   <table style="width:300px">
     <thead>
       <th>Name</th>
       <th>Location</th>
     </thead>
    <tbody data-bind="foreach: filteredPeople">
        <tr>
          <td>
                <span data-bind="text: name"> </span> 
          </td>
            <td>
                <span data-bind="text: country"> </span> 
          </td>
        </tr>
    </tbody>
  </table>

</div>

CSS

body { font-family: arial; font-size: 14px; }
.liveExample { padding: 1em; background-color: #EEEEDD; border: 1px solid #CCC; max-width: 655px; }
.liveExample input { font-family: Arial; }
.liveExample b { font-weight: bold; }
.liveExample p { margin-top: 0.9em; margin-bottom: 0.9em; }
.liveExample select[multiple] { width: 100%; height: 8em; }
.liveExample h2 { margin-top: 0.4em; }

.renderTime { color: #777; font-style: italic; font-size: 0.8em; }

li { list-style-type: disc; margin-left: 20px; }

JavaScript

// Define a "Person" class that tracks its own name and children, 
// and has a method to add a new child
var Person = function(name, country) {
    this.name = name;
    this.country = country;
}

// The view model is an abstract description of the state of the UI, 
// but without any knowledge of the UI technology (HTML)
var viewModel = {
    people: ko.observableArray([
        new Person("Annabelle", "UK"),
        new Person("Bertie", "UK"),
       new Person("Bertie", "USA"),
        new Person("Ali", "Bangladesh"),
        new Person("Patel", "India"),
      new Person("Sweatha", "India"),
       new Person("Minto", "India"),
        ]),
  selectedCountries: ko.observableArray()
};

viewModel.uniqueCountries = ko.computed(function() {
      var people = viewModel.people(),
          index = {},
          uniqueCountries= [],
          length = people.length,
          i, country;
  
      for (i = 0; i < length; i++) {
          country = people[i].country;      
          if (!index[country]) {
               index[country] = true;
               uniqueCountries.push(country);
          }
      } 
  
      return uniqueCountries;
});
  
viewModel.filteredPeople = ko.computed(function() {
  var selected = viewModel.selectedCountries() || [],
      index = {};
  
  //build an index, so we can avoid looping each time
  ko.utils.arrayForEach(selected, function(country) {
      index[country] = true;
  });
  
  return ko.utils.arrayFilter(viewModel.people(), function(person) {
      return index[person.country];
  });
});

ko.applyBindings(viewModel);