user472269
user472269

Reputation: 367

Apply filter on knockout reduce function

I have below code in my razor view to populate radio button,

 <!-- ko foreach: { data: ko.unwrap(cars).reduce(function (res, v, i) { res[i%2].push(v); return res; }, [[],[]]), as: 'cars' }  -->
    <div data-bind="foreach: cars">
       <label class="car">
         <div>
            <input type="radio" name="Carinfo.Name" data-bind="checked: $root.carId, checkedValue: Id, value: Id"><span data-bind="text: model"></span                                    
        </div>
          </label>
     </div>
 <!-- /ko -->
  1. Trying to understand what reduce function is doing here ko.unwrap(cars).reduce(function (res, v, i) { res[i%2].push(v); return res; }
  2. Can I filter cars observable array (like v.Make == 'Honda'), inside reduce function and returned filtered cars to the DOM to populate radio button

Upvotes: 0

Views: 650

Answers (1)

Regis Portalez
Regis Portalez

Reputation: 4860

First, you want to remove all this logic from the view and move it to a viewModel.
This will give you

  1. proper intellisense (auto complete, hovering functions gives info on them and all these IDE goodness).
  2. readability: you view will just look like that:

    <!-- ko foreach: { data: filteredCars -->

  3. Testability. You will be able to write unit tests on that view model property. While testing the view is particularly hard.

Now your answer:

Trying to understand what reduce function is doing here ko.unwrap(cars).reduce(function (res, v, i) { res[i%2].push(v); return res; }

ko.unwrap is a function which fetches the actual value of on object, no matter if it's observable or not. For example:

console.log(ko.unwrap(ko.observableArray([1, 2, 3])));
console.log(ko.unwrap([1, 2, 3]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

array reduce runs a callback against an array, and reduces all values inside an accumulator. To understand what this example is doing, let's run it on an easier example:

var cars = ["honda", "renault", "ford", "toyota", "volkswagen", "chevrolet", "volvo"];

var splitted = cars.reduce(function (res, v, i) { 
  res[i%2].push(v); return res; 
}, [[],[]]);

console.log(splitted);

It's basically splitting your array of cars into two arrays. First array with cars having even indexes, and second with odd indexes.

Can I filter cars observable array (like v.Make == 'Honda'), inside reduce function and returned filtered cars to the DOM to populate radio button

Yes you can: again, a simple fiddle:

// let's say this observable comes from another VM
var cars = ko.observableArray([{
  maker: "honda",
  country: "japan"
}, {
  maker: "renault",
  country: "france"
}, {
  maker: "ford",
  country: "us"
}, {
  maker: "toyota",
  country: "japan"
}, {
  maker: "volkswagen",
  country: "germany"
}, {
  maker: "chevrolet",
  country: "us"
}, {
  make: "volvo",
  country: "sweden"
}]);

var viewModel = function() {
  this.japaneseCars = ko.computed(function() {
    return ko.unwrap(cars).reduce(function(result, v, i) {
      if (v.country === "japan") {
        result.push(v.maker);
      }
      return result;
    }, []);
  }, this);
};

var vm = new viewModel();
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<div data-bind="foreach: japaneseCars">
  <input type="radio" name="cars" data-bind="attr: { value: $data }">
  <span data-bind=" text: $data " />
  <br />
</div>

Upvotes: 2

Related Questions