how should I keep a running total of several value

2019-07-21 09:46发布

问题:

Messing around with a bacon.js. I'd like to keep a running total of values in a group of text inputs. The example on the github site uses .scan and an adder function, which works fine for the example because it's using -1 and +1 in the stream. But I'd like values to be removed from the stream if they are edited, so the .scan solution won't really work for me or I'm not doing it right.

Markup:

<ul style="repeat-direction: vertical; list-style-type: none;">
  <li><input data-group="0"></li>
  <li><input data-group="0"></li>
  <li><input data-group="0"></li>
</ul>
<ul style="repeat-direction: vertical; list-style-type: none;">
  <li><input data-group="1"></li>
  <li><input data-group="1"></li>
  <li><input data-group="1"></li>
</ul>

So the solution that I have is to run through the inputs whenever there's a throttled keyup event and then update a span when it changes.

groupZeroEvents = events.filter (val) ->
   Math.floor(val[0]) == 0 # the stream contains the group the input belongs to 

groupZeroEvents.onValue (val) ->
    sum = 0
    $("input[data-group='0']").each (i,el) ->
      sum += Math.floor($(el).val())
    $('span#sum-g0').html(sum)

It works fine, but seems lame -- feels like I'm missing a way to use Bacon.js correctly.

回答1:

The sum depends on the current value of multiple inputs. If you model these inputs as Properties, you'll come to a nicer solution:

function sum(xs) { return _.reduce(xs, (function(x,y) {return x + y}), 0); }

// array of Properties representing the value of each group-zero-element
var groupZeroValues = $("input[data-group=0]").map(function(index, elem) {
     return $(elem).asEventStream("keyup")
            .map(function(e) { return parseInt($(e.target).val()); })
            .toProperty(0)
     }).toArray();

// sum Property
var groupZeroSum = Bacon.combineAsArray(groupZeroValues).map(sum)

// assign Property value to the "text" method of the sum element
groupZeroSum.assign($("#sum-g0"), "text")

I didn't have time to actually try this, but the idea will definitely work.