Search code examples
javascriptarraysrhino

Rhino 1.5 R5 not evaluating JavaScript correctly when finding array differences


I'm having a problem with some JavaScript which is being evaluated in an app using Rhino 1.5 R5 (Rhino 1.5 release 5 2004 03 25). I cannot update Rhino independently.

I have two arrays: the original data (pastedArr) and an array of only valid values (validatedArr) and I'm trying to find the difference (the "bad" values). I've found a function online to help (sym) and I've had to prototype forEach, but it works perfectly in jsfiddle (https://jsfiddle.net/zr3abc0c/).

In app, using Rhino, it doesn't return elements which are integers (even though they are stored as strings), like the script below and in the fiddle link which returns ["33"] on jsfiddle but null in my application. If parameter0 was ["33","1A","ABC"] it would return only ["1A","ABC"] in app.

I can't figure out where it is failing with these "integer" string elements.

// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: http://es5.github.io/#x15.4.4.18
if (!Array.prototype.forEach) {

  Array.prototype.forEach = function(callback, thisArg) {

    var T, k;

    if (this == null) {
      throw new TypeError(' this is null or not defined');
    }

    // 1. Let O be the result of calling toObject() passing the
    // |this| value as the argument.
    var O = Object(this);

    // 2. Let lenValue be the result of calling the Get() internal
    // method of O with the argument "length".
    // 3. Let len be toUint32(lenValue).
    var len = O.length >>> 0;

    // 4. If isCallable(callback) is false, throw a TypeError exception.
    // See: http://es5.github.com/#x9.11
    if (typeof callback !== "function") {
      throw new TypeError(callback + ' is not a function');
    }

    // 5. If thisArg was supplied, let T be thisArg; else let
    // T be undefined.
    if (arguments.length > 1) {
      T = thisArg;
    }

    // 6. Let k be 0
    k = 0;

    // 7. Repeat, while k < len
    while (k < len) {

      var kValue;

      // a. Let Pk be ToString(k).
      //    This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the HasProperty
      //    internal method of O with argument Pk.
      //    This step can be combined with c
      // c. If kPresent is true, then
      if (k in O) {

        // i. Let kValue be the result of calling the Get internal
        // method of O with argument Pk.
        kValue = O[k];

        // ii. Call the Call internal method of callback with T as
        // the this value and argument list containing kValue, k, and O.
        callback.call(T, kValue, k, O);
      }
      // d. Increase k by 1.
      k++;
    }
    // 8. return undefined
  };
}

function sym( /* pass one or more arrays here */ ) {
  var ans = [],
    cnts = {},
    currentMap;

  //count all items in the array
  for (var i = 0; i < arguments.length; i++) {
    currentMap = {};
    arguments[i].forEach(function(item) {
      // if we haven't already counted this item in this array
      if (!currentMap.hasOwnProperty(item)) {
        if (cnts.hasOwnProperty(item)) {
          // increase cnt
          ++cnts[item].cnt;
        } else {
          // initalize cnt and value
          cnts[item] = {
            cnt: 1,
            val: item
          };
        }
      }
      // keep track of whethere we've already counted this item in this array
      currentMap[item] = true;
    });
  }
  // output all items that have a cnt of 1
  for (var item in cnts) {
    if (cnts.hasOwnProperty(item) && cnts[item].cnt === 1) {
      ans.push(cnts[item].val);
    }
  }

  return ans;
}

function sort_uniq_fast(a) {
  var seen = {};
  var out = [];
  var len = a.length;
  var j = 0;
  for (var i = 0; i < len; i++) {
    var item = a[i];
    if (seen[item] !== 1) {
      seen[item] = 1;
      out[j++] = item;
    }
  }
  return out.sort();
}

function getBadCodes(pastedArr, validatedArr) {
  var result = sym(pastedArr, validatedArr);
  return sort_uniq_fast(result);
}

   var parameter0 = ["33"];
   var parameter1 = new Array();

alert(getBadCodes(parameter0,parameter1));


Solution

  • I don't have Rhino 1.5R5 lying around, but Rhino treats integer properties as special, because of their use in Array. I am speculating that there is a bug in this handling in Rhino 1.5R5.

    I would suggest preprocessing the items to compare and postprocessing the result, as I do in this answer (see prefix and unprefix).

    Let us know whether it works! (And I'll update the answer accordingly.)

    // Production steps of ECMA-262, Edition 5, 15.4.4.18
    // Reference: http://es5.github.io/#x15.4.4.18
    if (!Array.prototype.forEach) {
    
      Array.prototype.forEach = function(callback, thisArg) {
    
        var T, k;
    
        if (this == null) {
          throw new TypeError(' this is null or not defined');
        }
    
        // 1. Let O be the result of calling toObject() passing the
        // |this| value as the argument.
        var O = Object(this);
    
        // 2. Let lenValue be the result of calling the Get() internal
        // method of O with the argument "length".
        // 3. Let len be toUint32(lenValue).
        var len = O.length >>> 0;
    
        // 4. If isCallable(callback) is false, throw a TypeError exception.
        // See: http://es5.github.com/#x9.11
        if (typeof callback !== "function") {
          throw new TypeError(callback + ' is not a function');
        }
    
        // 5. If thisArg was supplied, let T be thisArg; else let
        // T be undefined.
        if (arguments.length > 1) {
          T = thisArg;
        }
    
        // 6. Let k be 0
        k = 0;
    
        // 7. Repeat, while k < len
        while (k < len) {
    
          var kValue;
    
          // a. Let Pk be ToString(k).
          //    This is implicit for LHS operands of the in operator
          // b. Let kPresent be the result of calling the HasProperty
          //    internal method of O with argument Pk.
          //    This step can be combined with c
          // c. If kPresent is true, then
          if (k in O) {
    
            // i. Let kValue be the result of calling the Get internal
            // method of O with argument Pk.
            kValue = O[k];
    
            // ii. Call the Call internal method of callback with T as
            // the this value and argument list containing kValue, k, and O.
            callback.call(T, kValue, k, O);
          }
          // d. Increase k by 1.
          k++;
        }
        // 8. return undefined
      };
    }
    
    function sym( /* pass one or more arrays here */ ) {
      var ans = [],
        cnts = {},
        currentMap;
    
      //count all items in the array
      for (var i = 0; i < arguments.length; i++) {
        currentMap = {};
        arguments[i].forEach(function(item) {
          // if we haven't already counted this item in this array
          if (!currentMap.hasOwnProperty(item)) {
            if (cnts.hasOwnProperty(item)) {
              // increase cnt
              ++cnts[item].cnt;
            } else {
              // initalize cnt and value
              cnts[item] = {
                cnt: 1,
                val: item
              };
            }
          }
          // keep track of whethere we've already counted this item in this array
          currentMap[item] = true;
        });
      }
      // output all items that have a cnt of 1
      for (var item in cnts) {
        if (cnts.hasOwnProperty(item) && cnts[item].cnt === 1) {
          ans.push(cnts[item].val);
        }
      }
    
      return ans;
    }
    
    function sort_uniq_fast(a) {
      var seen = {};
      var out = [];
      var len = a.length;
      var j = 0;
      for (var i = 0; i < len; i++) {
        var item = a[i];
        if (seen[item] !== 1) {
          seen[item] = 1;
          out[j++] = item;
        }
      }
      return out.sort();
    }
    
    function prefix(array) {
        var rv = [];
        for (var i=0; i<array.length; i++) {
          rv[i] = "A" + array[i];
        }
        return rv;
    }
    
    function unprefix(array) {
        var rv = [];
        for (var i=0; i<array.length; i++) {
          rv[i] = array[i].substring(1);
        }
        return rv;
    }
    
    function getBadCodes(pastedArr, validatedArr) {
      var result = unprefix(sym(prefix(pastedArr), prefix(validatedArr)));
      return sort_uniq_fast(result);
    }
    
       var parameter0 = ["33"];
       var parameter1 = new Array();
    
    alert(getBadCodes(parameter0,parameter1));