Parameter Normalization

How to normalize parameters

Posted by My on July 10, 2023

参数归一化(Parameter Normalization)是一种在处理数据时常用的技术,旨在将不同范围或不同单位的数据转换为一个统一的标准,使其在分析和建模时更加有效和一致。那在软件开发中如何实现参数归一化呢?

比如说有一个函数,功能很简单,把传入的一个日期进行格式化,返回一个字符串。

1
2
3
4
5
6
7
/**
 * @description 格式化日期
 * @param {Date} date 日期
 * @param {string} formatter 格式化字符串
 * @param {boolean} isPad 是否补零
 */
function formate(date, formatter, isPad) {}

但是格式化的方式有很多,比如可能调用的方式有:

  • 2023-7-10 传入一个日期,我要得到的仅仅是一个日期
    1
    
    formate(new Date(), "date");
    
  • 2023-7-10 8:00:30 传入一个日期,我要得到的既有日期,又有时间
    1
    
    formate(new Date(), "datetime");
    
  • 2023-07-10 传入一个日期,我要得到的仅仅是一个日期,并且在前面补零。

    1
    
    formate(new Date(), "date", true);
    
  • 2023-07-10 08:00:30 传入一个日期,我要得到的既有日期,又有时间,并且在前面补零。

    1
    
    formate(new Date(), "datetime", true);
    
  • 2023 年 7 月 10 日 13:12:35 传入一个日期,我要得到的既有日期,又有时间,并且按照指定的格式进行格式化。

    1
    
    formate(new Date(), "yyyy年MM月dd日 HH:mm:ss");
    
  • 2023 年 7 月 10 日 13:12:35 传入一个日期和一个函数,函数对前面的日期进行定制格式化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    formate(new Date("2023/7/10"), function (dateInfo) {
      const { year } = dateInfo;
      const thisYear = new Date().getFullYear();
      if (year < thisYear) {
        return `${thisYear - year}年前`;
      } else if (year > thisYear) {
        return `${year - thisYear}年后`;
      } else {
        return "今年";
      }
    });
    

formatter 参数的格式太多了,如果不处理的话,就需要做很多次判断,代码会变得很冗长,不利于维护。我们可以思考,有没有一种格式是涵盖了其他格式的,又可以很灵活地处理的呢?

很明显,函数,可以把其他的情况都转为函数的形式,这样就可以灵活地处理各种格式了。比如说第一种,是想返回一种格式的日期,那就可以这么处理:

1
2
3
4
5
// 2023-7-10
function formate(new Date(), (dateInfo)=>{
  const {...} = dateInfo;
  return `xxx-x-xx`
})

同理,其他情况都可以转化为函数的形式,这样就可以灵活地处理各种格式了。因此,我们在函数内部使用一个方法,将所有的情况都转化为函数的形式,这就是「参数归一化」。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
 * @description 参数归一化
 * @param {string | Function} formatter 格式化的内容
 * @param {boolean} isPad 是否补零
 * @returns {Function} 格式化函数
 */
function _formateNormalize(formatter) {
  if (typeof formatter === "function") {
    return formatter;
  }
  if (typeof formatter !== "string") {
    return new TypeError("formatter must be a string or a function");
  }

  if (formatter === "date") {
    formatter = "yyyy-MM-dd";
  }
  if (formatter === "datetime") {
    formatter = "yyyy-MM-dd HH:mm:ss";
  }
  //处理字符串,比如"yyyy年MM月dd日 HH:mm:ss",将对应位置的替换成具体的值
  function formatFun(dateInfo) {
    const { yyyy, MM, dd, HH, mm, ss } = dateInfo;
    return formatter
      .replaceAll("yyyy", yyyy)
      .replaceAll("MM", MM)
      .replaceAll("dd", dd)
      .replaceAll("HH", HH)
      .replaceAll("mm", mm)
      .replaceAll("ss", ss);
  }
  return formatFun;
}

到此,参数归一化就完成了。在使用的过程中,传入一个格式化字符串,就可以得到一个格式化函数(上面的),然后就可以使用这个函数进行格式化了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function formate(date, formatter, isPad) {
  formatter = _formateNormalize(formatter); // 得到的格式化函数

  const dateInfo = {
    year: date.getFullYear(),
    month: date.getMonth() + 1,
    date: date.getDate(),
    hours: date.getHours(),
    minutes: date.getMinutes(),
    seconds: date.getSeconds(),
  };

  // 字符串化
  dateInfo.yyyy = dateInfo.year.toString();
  dateInfo.MM = dateInfo.month.toString();
  dateInfo.dd = dateInfo.date.toString();
  dateInfo.HH = dateInfo.hours.toString();
  dateInfo.mm = dateInfo.minutes.toString();
  dateInfo.ss = dateInfo.seconds.toString();
  console.log("dateInfo", dateInfo);

  // 补零
  function pad(prop, len) {
    dateInfo[prop] = dateInfo[prop].padStart(len, "0");
  }
  if (isPad) {
    pad("yyyy", 4);
    pad("MM", 2);
    pad("dd", 2);
    pad("HH", 2);
    pad("mm", 2);
    pad("ss", 2);
  }

  // 进行格式化
  return formatter(dateInfo);
}

到此,整个日期格式化函数就完成了。