【问题标题】:How to convert string to array with ranges JavaScript如何使用范围 JavaScript 将字符串转换为数组
【发布时间】:2019-06-26 22:41:48
【问题描述】:

考虑以下字符串:7, 20, 22, 30–32, 33, 36–40, 46

我开发了一些代码,它会自动将所述字符串解析为具有给定范围的数组,如下所示。

注意:一个典型的用例是 - 在一个包含 100 页的 pdf 中搜索选定的页面

var number_string = "7, 20, 22, 30–32, 33, 36–40, 46".toString().replace(/–/gi, '-').replace(/ /gi, '').split(',');
var new_arr = [];

$.each(number_string, function(index, value) {
  if (value.match(/-/gi)) {
    var range_arr = value.split('-');
    var sub_arr = range(range_arr[0], range_arr[1]);
    $.each(sub_arr, function(sub_index, sub_value) {
      new_arr.push(parseInt(sub_value, 10));
    });
  } else {
    new_arr.push(parseInt(value, 10));
  }
});

console.log(new_arr);

function range(lowEnd, highEnd) {
  var arr = [],
    c = highEnd - lowEnd + 1;
  while (c--) {
    arr[c] = highEnd--
  }
  return arr;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

是否有一种更精简的非 jQuery 方法可以在这里使用,而且简单、重量轻且易于阅读?请不要使用 ES6 的东西,因为这对我来说是希腊语。

注意:ToInt 函数只是一个返回有效数字或 0 的函数。

【问题讨论】:

  • 我看到你只使用each函数形式的jQuery。你可以改用for of
  • 所以,是的,ToInt ...呵呵 有什么理由不喜欢parseInt(val, 10)
  • 如果 ES6 是希腊语,jQuery 是 Pig Latin。
  • 首先,由于这是您想要改进的工作代码,您应该在Code Review 提出这个问题(他们是关于改进代码,我们是关于生成创建工作代码)。第二:如果“ES6 的东西……对 [你] 来说是希腊语” 认为这是学习 ES6 的机会和理由(ECMAScript 目前似乎在每年更新一次,具体取决于浏览器实施;如果你推迟它,它不会变得更容易学习)。
  • @Certain:鉴于问题似乎是“此代码有效,如何改进”我写了该建议以产生与问题中产生的最终结果相同的最终结果。鉴于我们都 - 明确 - 做出假设,OP需要澄清。

标签: javascript jquery arrays


【解决方案1】:

jQuery.map() 方法的作用类似于平面地图(返回的子数组是扁平的)。在地图的回调函数中,使用String.search() 检查是否有破折号。如果不使用 + 运算符转换为数字并返回。如果有破折号,拆分,使用for循环将最小值和最大值转换为数组,并返回数组。

function convert(str) {
  var arr = jQuery.map(str.split(', '), function(s) {
    if(s.search('–') === -1) return +s;
    
    var minmax = s.split('–');
    var range = [];
    
    for(var i = +minmax[0]; i <= +minmax[1]; i++) range.push(i);
    
    return range;
  });
  
  return arr;
}

var number_string = "7, 20, 22, 30–32, 33, 36–40, 46";

var result = convert(number_string);
  
console.log(result);
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"&gt;&lt;/script&gt;

使用 ESNext 我将使用Array.flatMap() 代替jQuery.map(),使用String.inclues() 检测破折号,使用Array.from() 生成子数组。

const convert = (str) => 
  str.split(', ')
    .flatMap(s => {
      if(!s.includes('–')) return +s;
      
      const [min, max] = s.split('–');
      
      return Array.from({ length: max - min + 1 }, (_, n) => n + +min);
    });

var number_string = "7, 20, 22, 30–32, 33, 36–40, 46";

var result = convert(number_string, '–');
  
console.log(result);

【讨论】:

  • 我是浏览器兼容性的坚持者,请摆脱 indexOf 并使用jQuery.inArray()
  • jQuery.inArray 在这里不起作用,因为我使用的是 String.indexOf。转换为 String.search。
  • .search() 只是 IE12。如果只是字符串搜索,我们不能只使用.match().test() 与正则表达式或其他东西。这绝对是我一直在寻找的答案
  • 我们的平台支持IE9
  • IE6及以上版本支持搜索。
【解决方案2】:

我会使用.reduce。首先用逗号分割初始字符串。如果被迭代的字符串没有-,那么只需将数字推送到累加器;否则,除以- 得到lowhigh 数字,然后使用for 循环将所有数字从low 推送到high 到累加器:

const ToInt = Number;

const numArr = "7, 20, 22, 30–32, 33, 36–40, 46".split(', ');
const result = numArr.reduce((a, str) => {
  if (!str.includes('–')) {
    a.push(ToInt(str));
    return a;
  }
  const [low, high] = str.split('–');
  for (let i = Number(low); i <= high; i++) {
    a.push(i);
  }
  return a;
}, []);
console.log(result);

如果由于某种原因不想使用 ES6,可以使用Babel 将其转换为 ES5:

"use strict";

function _slicedToArray(arr, i) {
  return (
    _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest()
  );
}

function _nonIterableRest() {
  throw new TypeError("Invalid attempt to destructure non-iterable instance");
}

function _iterableToArrayLimit(arr, i) {
  var _arr = [];
  var _n = true;
  var _d = false;
  var _e = undefined;
  try {
    for (
      var _i = arr[Symbol.iterator](), _s;
      !(_n = (_s = _i.next()).done);
      _n = true
    ) {
      _arr.push(_s.value);
      if (i && _arr.length === i) break;
    }
  } catch (err) {
    _d = true;
    _e = err;
  } finally {
    try {
      if (!_n && _i["return"] != null) _i["return"]();
    } finally {
      if (_d) throw _e;
    }
  }
  return _arr;
}

function _arrayWithHoles(arr) {
  if (Array.isArray(arr)) return arr;
}

var ToInt = Number;
var numArr = "7, 20, 22, 30–32, 33, 36–40, 46".split(", ");
var result = numArr.reduce(function(a, str) {
  if (str.indexOf("–") === -1) {
    a.push(ToInt(str));
    return a;
  }

  var _str$split = str.split("–"),
    _str$split2 = _slicedToArray(_str$split, 2),
    low = _str$split2[0],
    high = _str$split2[1];

  for (var i = Number(low); i <= high; i++) {
    a.push(i);
  }

  return a;
}, []);
console.log(result);

(但 ES6 版本更简洁,可能很多更容易阅读和理解)

【讨论】:

  • 您的.reduce() 回调可以更短。 (a, str) =&gt; { a.push(...str.split('–').map(ToInt)); return a; };.concat() 使其更短。
  • 我没有提到它是我的错,但如果我没记错的话,它需要超级浏览器兼容(reduce 不是)。
  • @ziggywiggy 虽然这适用于问题中的exact 输入,但不适用于ranges。我假设,例如47-49,他希望推送 47、48 和 49。只是拆分- 并同时推送两者只会推送4749
  • @AlexanderDixon Array.prototype.reduce 来自ES 5.1。如果您的用户正在运行甚至不支持 ES5 的浏览器,我认为这是用户的问题,而不是您的脚本:P
  • @CertainPerformance const ToInt = Number ...当然不是。 :) 1.1 一个数字,但亲爱的,不是一个整数。宁可使用ToInt = parseInt ... 或者最好扔掉那些废话,去巧克力香草味
【解决方案3】:

为此我有自己的实现 parseStringRange 接受 '1,2,3' 和 '1,2,3-5,6,7' 之类的字符串,还删除无效字符(不是数字或 , 或 -)并返回一个包含所有数字的数组。

该函数还返回有序的数字并去除重复,用户也可以输入范围内的无序数字,'1-5'和'5-1'将返回相同的输出。

const parseStringRange = (range = '') =>
`${range}`
    ?.replace(/[^0-9,-]/g, '') //remove invalid chars
    ?.split(',') //convert 1,2 to [1, 2]
    ?.flatMap(s => { //convert 1-3 to [1,2,3]
        if (!s.includes('-')) return [s]

        const [min, max] = s.split('-').map(e => Number(e))

        if (min > max) var i = -1
        else var i = 1

        const r = []

        for (let p = min; p != max; p += i)
            r.push(p)

        r.push(max)

        return r;
    })
    ?.map(e => Number(e)) //convert all string numbers to number
    ?.reduce((t, e, i, a) => {
        if (!t)
            t = [... new Set(a)]
        return t
    }, null) //remove duplicates
    ?.sort((a, b) => a - b) //sort numbers, smallest first

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-06
    • 1970-01-01
    • 2015-11-11
    • 1970-01-01
    • 2012-04-08
    相关资源
    最近更新 更多