【问题标题】:Is there a better way to extract information from a string?有没有更好的方法从字符串中提取信息?
【发布时间】:2019-05-28 18:34:43
【问题描述】:

假设我有一个字符串数组,我需要它们的特定信息,有什么简单的方法可以做到这一点?

假设数组是这样的:

let infoArr = [
  "1 Ben Howard 12/16/1988 apple",
  "2 James Smith 1/10/1999 orange",
  "3 Andy Bloss 10/25/1956 apple",
  "4 Carrie Walters 8/20/1975 peach",
  "5 Doug Jones 11/10/1975 peach"
];

假设我想提取日期并将其保存到另一个数组中,那么我可以制作这样的函数

function extractDates(arr)
{
  let dateRegex = /(\d{1,2}\/){2}\d{4}/g, dates = "";
  let dateArr = [];

  for(let i = 0; i<arr.length; i++)
  {
    dates = /(\d{1,2}\/){2}\d{4}/g.exec(arr[i])
    dates.pop();
    dateArr.push(dates);
  }

  return dateArr.flat();
}

虽然这可行,但它很笨重并且需要pop(),因为它会返回一个数组数组,即:["12/16/1988", "16/"],另外我需要在之后调用flat()

另一种选择是使用给定位置对字符串进行子串化,我需要知道正则表达式模式。

function extractDates2(arr)
{
  let dates = [];

  for(let i = 0; i<arr.length; i++)
  {
    let begin = regexIndexOf(arr[i], /(\d{1,2}\/){2}\d{4}/g);
    let end = regexIndexOf(arr[i], /[0-9] /g, begin) + 1;
    dates.push(arr[i].substring(begin, end));
  }

  return dates;
 }    

当然它使用下一个regexIndexOf()函数:

function regexIndexOf(str, regex, start = 0)
{
  let indexOf = str.substring(start).search(regex);
  indexOf = (indexOf >= 0) ? (indexOf + start) : -1;
  return indexOf;
}

同样,这个函数也可以工作,但完成简单的提取似乎太糟糕了。有没有更简单的方法将数据提取到数组中?

【问题讨论】:

  • 为什么不使用array.map?
  • @HenryHoweson .map(单独)如果输入字符串中的日期不止一个,例如"1 Ben Howard 12/16/1988 apple 1/10/1999 ",则将不起作用,他必须在之后将其展平
  • @CertainPerformance 设置正则表达式全局标志后,它可以正常工作,而且 OP 似乎也不需要这样做。
  • @HenryHoweson 仅使用全局标志不允许(简洁)将匹配项从 multiple 字符串提取到 single 数组中,我想?你在想什么代码?

标签: javascript arrays regex


【解决方案1】:

一种方法是在数组的元素上使用map(),对每个元素应用匹配,最后调用flat() 以获得所需的结果:

let infoArr = [
  "1 Ben Howard 12/16/1988 apple",
  "2 James Smith 1/10/1999 orange",
  "3 Andy Bloss 10/25/1956 apple",
  "4 Carrie Walters 8/20/1975 peach",
  "5 Doug Jones 11/10/1975 peach"
];

const result = infoArr.map(o => o.match(/(\d{1,2}\/){2}\d{4}/g)).flat();

console.log(result);

或者,您可以使用flatMap()

let infoArr = [
  "1 Ben Howard 12/16/1988 apple",
  "2 James Smith 1/10/1999 orange",
  "3 Andy Bloss 10/25/1956 apple",
  "4 Carrie Walters 8/20/1975 peach",
  "5 Doug Jones 11/10/1975 peach"
];

const result = infoArr.flatMap(o => o.match(/(\d{1,2}\/){2}\d{4}/g));

console.log(result);

另外,如果您需要从最终数组中删除 null 值,以防有没有日期的字符串,您可以应用 filter(),如下所示:

const result = infoArr.map(o => o.match(/(\d{1,2}\/){2}\d{4}/g))
                      .flat()
                      .filter(date => date !== null);

const result = infoArr.flatMap(o => o.match(/(\d{1,2}\/){2}\d{4}/g))
                      .filter(date => date !== null);

数据冲突示例:

let infoArr = [
  "1 Ben Howard 12/16/1988 apple 10/22/1922",
  "2 James Smith orange",
  "3 Andy Bloss 10/25/1956 apple",
  "4 Carrie Walters 8/20/19075 peach",
  "5 Doug Jones 11/10-1975 peach"
];

const result = infoArr.flatMap(o => o.match(/(\d{1,2}\/){2}\d{4}/g))
                      .filter(date => date !== null); /* or filter(date => date) */

console.log(result);

没有 flat() 的替代方案:

由于 flat()flatMap() 目前仍处于“实验性”状态,可能会发生变化,并且某些浏览器(或版本)不支持它,您可以使用下一个替代方案,但限制只能获得第一个匹配项在每个string:

const infoArr = [
  "1 Ben Howard 12/16/1988 apple 10/22/1922",
  "2 James Smith orange",
  "3 Andy Bloss 10/25/1956 apple",
  "4 Carrie Walters 8/20/19075 peach",
  "5 Doug Jones 11/10-1975 peach"
];

const getData = (input, regexp, filterNulls) =>
{
    let res = input.map(o =>
    {
        let matchs = o.match(regexp);
        return matchs && matchs[0];
    });

    return filterNulls ? res.filter(Boolean) : res;
}

console.log(getData(infoArr, /(\d{1,2}\/){2}\d{4}/g, false));
console.log(getData(infoArr, /(\d{1,2}\/){2}\d{4}/g, true));

【讨论】:

  • 如果任何行不包含日期,您将在数组中得到null 值。
  • @Shidersz 添加 .filter((e) => {return e}) 到末尾
  • 是的,谢谢,我正在写这个,但我使用了更易读的语法
  • 另外,请记住,flatflatMap 目前仍处于“试验阶段”,可能会发生变化。
【解决方案2】:

一种选择是通过不匹配的分隔符连接字符串,例如,,然后执行全局匹配以从中获取日期数组:

let infoArr = [
  "1 Ben Howard 12/16/1988 apple",
  "2 James Smith 1/10/1999 orange",
  "3 Andy Bloss 10/25/1956 apple",
  "4 Carrie Walters 8/20/1975 peach",
  "5 Doug Jones 11/10/1975 peach"
];
const result = infoArr
  .join(',')
  .match(/(\d{1,2}\/){2}\d{4}/g);
console.log(result);

【讨论】:

  • 效果很好,而且非常简洁易懂。
  • 这个解决方案似乎是最快的:jsben.ch/w9geK 另外它的优点是它可以处理没有日期的数组元素(不会在数组中创建空值),但是请记住,如果您正在尝试通过基于原始数组的索引来获取特定元素的日期,那么如果某些元素没有日期,它可能不会对齐
  • 这在infoArr 为空或没有字符串包含日期时不起作用,因为match() 返回null 而不是数组。这里没有理由使用joinmapflatMap 更合理。
【解决方案3】:

虽然这可行,但它很笨重并且需要pop(),因为它会返回一个数组数组,即:["12/16/1988", "16/"],另外我需要在之后调用flat

正则表达式 exec 方法始终在 0 属性中匹配(假设它完全匹配),您可以访问它并将其推送到您的数组:

let infoArr = [
  "1 Ben Howard 12/16/1988 apple",
  "2 James Smith 1/10/1999 orange",
  "3 Andy Bloss 10/25/1956 apple",
  "4 Carrie Walters 8/20/1975 peach",
  "5 Doug Jones 11/10/1975 peach"
];

function extractDates(arr){
  const dateRegex = /(\d{1,2}\/){2}\d{4}/g;
  const dateArr = [];
  for (const str of arr){
    const date = /(\d{1,2}\/){2}\d{4}/g.exec(str);
    dateArr.push(date[0]);
  }
  return dateArr;
}

console.log(extractDates(infoArr));

(当然你也可以在map回调中做同样的事情)

【讨论】:

    【解决方案4】:

    您可以使用reduce() 而不是循环来配对代码。如果没有匹配项,请注意将 null 保留在数组之外。

    let infoArr = [
        "1 Ben Howard 12/16/1988 apple",
        "2 James Smith 1/10/1999 orange",
        "3 Andy Bloss 10/25/1956 apple",
        "4 Carrie Walters 8/20/1975 peach",
        "5 Doug Jones 11/10/1975 peach"
      ];
      
    let regex = /(\d{1,2}\/){2}\d{4}/g
    let dates =  infoArr.reduce((arr, s) => arr.concat(s.match(regex) || []) , [])
    console.log(dates)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-28
      • 1970-01-01
      • 2011-10-11
      • 2011-01-01
      • 1970-01-01
      • 2015-01-25
      相关资源
      最近更新 更多