间隔验证

Interval Validation

本文关键字:验证      更新时间:2023-09-26

我们有三个不同的网页,其中包含网格,用于添加主实体的详细信息。

Detail对象可以通过用javascript表示

var detail = {
     Description: 'Detail',
     MinPercentage: 0,
     MaxPercentage: 20
}

现在,我们希望在发送到服务器之前验证这些详细信息。

验证

  • 不得有任何交叉点。即细节(‘细节1’,0,20)和细节(‘详情2’,15,30)无效,因为15和20之间是共同的
  • 细节包含从给定最小值到给定最大值的值。即细节(‘细节1’,0,20)和细节(‘详情2’,20,40)保持0到40的值。如果给定的最小值为0,给定的最大值为40,则它们是有效的

职能部门的期望

  • 因为我想编写一个在多个地方使用的函数,所以应该尽可能通用

然后,我编写了一个名为areIntervalsValid的函数,但我不知道如何处理输入错误的调用、抛出异常、返回最佳结构化结果,我还想知道执行验证的最佳方式是什么。


// Returns array of detail object to test.
var getDetails = function () {
    var detail1 = { Description: 'Detail1', MinPercentage: 0, MaxPercentage: 20 }
    var detail2 = { Description: 'Detail2', MinPercentage: 40, MaxPercentage: 60 }
    var detail3 = { Description: 'Detail3', MinPercentage: 60, MaxPercentage: 72 }
    var detail4 = { Description: 'Detail4', MinPercentage: 72, MaxPercentage: 100 }
    var detail5 = { Description: 'Detail5', MinPercentage: 20, MaxPercentage: 40 }
    return new Array(detail1, detail2, detail3, detail4, detail5);
}
// Performs type checking, logical validation, and requirements validation.
var areIntervalsValid = function (items, min, max, minProperty, maxProperty) {
    // Returned object.
    var result = {
        Success: false,
        Message: ''
    }
    // Checks arguments have expected types.
    var validateFunctionCall = function () {
        if (!Array.isArray(items) || typeof min !== 'number' || typeof max !== 'number' || typeof minProperty !== 'string' || typeof maxProperty !== 'string')
            throw 'An error occurred while processing validation.';
        if (!items.length || min > max)
            throw 'An error occurred while processing validation.';
    }
    // Checks [minProperty] of detail that has minimum [minProperty] == min
    // and [maxProperty] of detail that has maximum [minProperty]
    var validateIntervalBasics = function () {
        if (items[0][minProperty] != min || items[items.length - 1][maxProperty] != max)
            throw 'Start and end values of interval do not match minimum - maximum values.';
    }
    // Checks @item has [minProperty] and [maxProperty].
    var validateHasProperty = function (item) {
        if (!item.hasOwnProperty(minProperty) || !item.hasOwnProperty(maxProperty)) {
            throw 'An error occurred while processing validation.';
        }
    }
    try {
        validateFunctionCall();
        // Sorts array of details in according to [minProperty].
        items.sort(function (item1, item2) { return item1[minProperty] > item2[minProperty] });
        validateIntervalBasics();
        var totalDiff = 0, currentItem;
        // Algorithm part. 
        for (var i = 0; i < items.length; i++) {
            currentItem = items[i];
            validateHasProperty(currentItem);
            totalDiff += currentItem[maxProperty] - currentItem[minProperty];
            if (i != items.length - 1 && currentItem[maxProperty] > items[i + 1][minProperty]) { // Finds intersections.
                throw "There are intersected values: " + currentItem[maxProperty] + " - " + items[i + 1][minProperty];
            }
        }
        // Checks second validation.
        if (totalDiff != max - min) {
            throw 'Total interval sum is not equal to ' + (max - min);
        }
        result.Success = true;
        return result;
    } catch (e) {
        console.log(e);
        result.Message = e;
        return result;
    }
}

然后,我调用这样的函数:

areIntervalsValid(getDetails(), 0, 100, "MinPercentage", "MaxPercentage");

我可以对函数做些什么,使其更加可靠、通用和快速?

如果您使用函数式编程原理,尤其是递归,可以用更好的方式实现这一点。

这是我解决这个问题的办法。没有数据类型验证逻辑,因为我相信你可以自己做:

// Numbers and intervals comparison logic
function intervalsIntersect(start1, end1, start2, end2) {
  return inBetween(start1, start2, end2) || inBetween(end1, start2, end2);
}
function inBetween(value, start, end){
  return Math.max.apply(null, arguments) != value && Math.min.apply(null, arguments) != value;
}
// Validation logic
function getDetailsIntersectionReport(interval1, interval2) {
  var comparisonResult = intervalsIntersect(interval1.MinPercentage, interval1.MaxPercentage, interval2.MinPercentage, interval2.MaxPercentage);
  return comparisonResult ? ('[' + interval1.Description + ' instersects with ' + interval2.Description + '], ') : '';
}
function compareHeadWithTailFunctionFactory(head, comparatorFunction) {
  return function ( previous, item) {
    return previous + comparatorFunction(head, item);
  }
}
// you have to inject custom comparator function to make this function generic
function validateWithReport(list, comparatorFunction) {
  if (list.length <= 1) { // return if there is nothing to compare
    return '';
  }
  var head = list[0];
  var tail = list.slice(1);
  return tail.reduce(compareHeadWithTailFunctionFactory(head, comparatorFunction), 
  '' // initial value - empty string
  ) + validateWithReport(tail, comparatorFunction);
}
function validateIntervals(intervals) {
  var result = validateWithReport(intervals, getDetailsIntersectionReport);
  if (result.length) {
    throw new Error('There are intersecting intervals: ' + result);
  }
  return true;
}
// Unit test with Jasmine
describe('validation with report', function() {
  
  var intervalsWithoutIntersections = [
    { Description: 'Detail1', MinPercentage: 0, MaxPercentage: 20 },
    { Description: 'Detail2', MinPercentage: 40, MaxPercentage: 60 },
    { Description: 'Detail3', MinPercentage: 60, MaxPercentage: 72 }
  ];
  var intervalsWithIntersections = [
    { Description: 'Detail4', MinPercentage: 0, MaxPercentage: 21 },
    { Description: 'Detail5', MinPercentage: 20, MaxPercentage: 60 },
    { Description: 'Detail6', MinPercentage: 60, MaxPercentage: 72 }
  ];  
  
  it('should report with exception about error', function() {
    expect( function() { // wrapping into closure to catch error properly
      validateIntervals(intervalsWithIntersections)
    }).toThrowError();
  });
  it('should report validation with true', function() {
    expect(validateIntervals(intervalsWithoutIntersections)).toBeTruthy();
  });
});