像excel一样规律填充(二)

最普遍的填充就是没有规律的时候复制对应位置的内容,可以标记为:
NORMAL_RULE = ({ value }) => {return value;};

一、数字类型

数字类型的填充可以归结为一点:最小二乘拟合。因此你需要知道什么是最小二乘拟合(https://baike.baidu.com/item/%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%98%E6%B3%95/2522346?fromtitle=%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%98%E6%8B%9F%E5%90%88&fromid=6260453&fr=aladdin).


/*
	numberList: 找规律的数据,是一个数组
	return: 返回的是一个填充的规律
		规律接受参数为填充的第几项
*/
getLeastSquares(numberList) {
    let slope, intercept, xAverage, yAverage;
    let xSum = 0, ySum = 0, xSquareSum = 0, xySum = 0;
    let validCellsLen = 0;
    let emptyCellPositions = [];
    numberList.forEach((v, i) => {
      if (v !== undefined && v !== null && v !== '') {
        validCellsLen++;
        xSum += i;
        ySum += v;
        xySum += (v * i);
        xSquareSum += Math.pow(i, 2);
      } else {
        emptyCellPositions.push(i);
      }
    });
    if (validCellsLen < 2) { // 长度小于2的,直接认为是复制对应位置的内容
      return NORMAL_RULE;
    }
    xAverage = xSum / validCellsLen;
    yAverage = ySum / validCellsLen;
    slope = (xySum - validCellsLen * xAverage * yAverage) / (xSquareSum - validCellsLen * Math.pow(xAverage, 2));
    intercept = yAverage - slope * xAverage;
    return ({ n }) => {
      if (emptyCellPositions.length && emptyCellPositions.includes(n % numberList.length)) {
        return '';
      }
      let y = n * slope + intercept; // 拟合公式:y = k * x + b
      return Number(parseFloat(y).toFixed(8)); 
    };
  }

二、文本类型

文本类型需要注意的是当遇见数字的时候,遇见数字时,如果仅是数字部分不同,要按照数字部分的规律进行填充


  const REG_STRING_NUMBER_PARTS = /\d+|\D+/g; // 将字符串进行切分
  const REG_NUMBER_DIGIT = /\d/; // 匹配数字
  
  // 是不是顺序数列
  function isArithmeticSequence(numberList) {
    let number0 = numberList[0];
    let tolerance = numberList[1] - number0;
    let func = (v, n) => {
      return v === n * (tolerance) + number0;
    };
    return numberList.every(func);
  }

  function getTextItemStructureInfo(textItem) {
    let validTextItem = textItem || '';
    let lastNumberPosition = -1, lastNumber = validTextItem;
    let valueList = validTextItem.match(REG_STRING_NUMBER_PARTS); // 是否存在数字部分
    for (let i = valueList.length - 1; i > -1; i--) {
      let valueItem = valueList[i];
      if (REG_NUMBER_DIGIT.test(valueItem)) { // 寻找最末尾的数字部分下标
        lastNumberPosition = i;
        lastNumber = valueItem;
        break;
      }
    }
    if (lastNumberPosition !== -1) {
      valueList[lastNumberPosition] = '-|*|-|*|-|*|-'; // 标识无特殊意义
    }
    
    return {lastNumberPosition, lastNumber, structure: valueList.join('')};
  }

  function getTextFillNumberRule(valueList, lastNumber, lastNumberPosition, fillFunc) {
    let isStartWith0 = lastNumber.startsWith('0');
    return ({ n }) => {
      let fillValue = fillFunc ? fillFunc({lastNumber, n}) : '';
      if (isStartWith0 && fillValue.length < lastNumber.length) {
        fillValue = '0'.repeat(lastNumber.length - fillValue.length) + fillValue;
      }
      valueList[lastNumberPosition] = fillValue;
      return valueList.join('');
    }; 
  }

  function getTextRule(textList) {
    let isAllNotIncludeNumber = textList.every(item => !REG_NUMBER_DIGIT.test(item || ''));
    if (isAllNotIncludeNumber) {
      return NORMAL_RULE;
    }
    if (textList.length === 1) {
      let valueList = textList[0].match(REG_STRING_NUMBER_PARTS);
      let { lastNumberPosition, lastNumber } = getTextItemStructureInfo(textList[0]);
      return getTextFillNumberRule(valueList, lastNumber, lastNumberPosition, ({lastNumber, n}) => {
        let lastNumberValue = parseInt(lastNumber, 10);
        return (lastNumberValue + n) + '';
      });
    }
    // isStructureConsistent: the last number part is not equal, other is equal
    let structureList = textList.map((text) => getTextItemStructureInfo(text));
    let firstStructure = structureList[0];
    let isStructureConsistent = structureList.every(structure => structure['lastNumberPosition'] === firstStructure['lastNumberPosition'] && structure['structure'] === firstStructure['structure']);
    if (isStructureConsistent) {
      let numberList = structureList.map(structure => parseInt(structure.lastNumber, 10));
      if (isArithmeticSequence(numberList)) {
        let valueList = textList[0].match(REG_STRING_NUMBER_PARTS);
        let secondStructure = structureList[1];  
        let secondStructureLastNumberValue = parseInt(secondStructure['lastNumber'], 10);
        return getTextFillNumberRule(valueList, firstStructure['lastNumber'], firstStructure['lastNumberPosition'], ({lastNumber, n}) => {
          let lastNumberValue = parseInt(lastNumber, 10);
          return (n * (secondStructureLastNumberValue - lastNumberValue) + lastNumberValue) + '';
        });
      }
      return NORMAL_RULE;
    }
    return ({value, n}) => {
      if (REG_NUMBER_DIGIT.test(value || '')) {
        let valueList = value.match(REG_STRING_NUMBER_PARTS);
        let { lastNumberPosition, lastNumber } = getTextItemStructureInfo(value);
        let isStartWith0 = lastNumber.startsWith('0');
        let lastNumberValue = parseInt(lastNumber, 10);
        let fillValue = (lastNumberValue + Math.floor(n / textList.length)) + '';
        if (isStartWith0 && fillValue.length < lastNumber.length) {
          fillValue = '0'.repeat(lastNumber.length - fillValue.length) + fillValue;
        }
        valueList[lastNumberPosition] = fillValue;
        return valueList.join('');
      }
      return value;
    };
  }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值