JavaScript中使用数组模式连接两个元素的简单方法

2w3rbyxf  于 2023-04-10  发布在  Java
关注(0)|答案(7)|浏览(211)

我有一个字符串数组,它们要么是一个星号("*"),要么是其他字符。
我想“合并”连续的星号如下:
如果一行中有一个或多个星号,则将它们从数组中删除,并在下一个非星号元素前添加一个 * 单个 * 星号。如果星号位于数组的末尾,则只需删除它们。
一些较小的例子:

[..., "*", "4", ...]      => [..., "*4", ...] // concat asterisk with string at the right
[..., "*", "*", "4", ...] => [..., "*4", ...] // remove first asterisk
[..., "*", "4", "*"]      => [..., "*4"]      // remove last asterisk
[..., "4", "*", "*", "*"] => [..., "4" ]      // remove all asterisk which have no alphanumeric at its right

对于此全尺寸输入:

let data = ["0", "1", "2", "3", "4", "5", "6", "*", "*", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "*", "34", "*", "36", "37", "*", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "*", "54", "*", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "*", "*", "*", "*", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "*", "91", "92", "93", "94", "95", "*", "97", "*", "*"];

结果应为:

["0", "1", "2", "3", "4", "5", "6", "*9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "*34", "*36", "37", "*39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "*54", "*56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "*78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "*91", "92", "93", "94", "95", "*97"]

如何实现这一点?以下是我的尝试:

let data = ["0", "1", "2", "3", "4", "5", "6", "*", "*", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "*", "34", "*", "36", "37", "*", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "*", "54", "*", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "*", "*", "*", "*", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "*", "91", "92", "93", "94", "95", "*", "97", "*", "*"];
let output = data.map((d, index, arr) => arr[index - 1] === '*' ? '*'+d : d).filter(d => d !== '*');
console.log(output);
ehxuflar

ehxuflar1#

您可以在一个循环中完成此操作,只需检查当前元素是否为*,如果不是,则为push(),如果前一个元素为*,则连接。

function collapse(array) {
  const result = [];

  for (const [i, n] of array.entries()) {
    if (n !== '*') {
      result.push(array[i - 1] === '*' ? `*${n}` : n);
    }
  }

  return result;
}

console.log(...collapse(['*', 1, 2, '*', 3]));
console.log(...collapse(['*', '*', 2, '*', 3]));
console.log(...collapse(['*', '*', 2, '*', '*']));
console.log(...collapse(["0", "1", "2", "3", "4", "5", "6", "*", "*", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "*", "34", "*", "36", "37", "*", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "*", "54", "*", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "*", "*", "*", "*", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "*", "91", "92", "93", "94", "95", "*", "97", "*", "*"]));

如果您愿意,也可以在reduce()调用中使用相同的逻辑

const input = ["0", "1", "2", "3", "4", "5", "6", "*", "*", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "*", "34", "*", "36", "37", "*", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "*", "54", "*", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "*", "*", "*", "*", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "*", "91", "92", "93", "94", "95", "*", "97", "*", "*"];

const result = input.reduce((a, n, i, { [i - 1]: prev }) => {
  if (n !== '*') {
    a.push(prev === '*' ? `*${n}` : n);
  }
  return a;
}, []);

console.log(...result);

或者,您可以通过在每次迭代时存储前一个值来避免串联中的第二个条件(以及第二次数组访问)。这类似于函数中的状态机答案,但避免了重复的解构和对象创建。

function collapse(array) {
  const result = [];

  let prev = '';
  for (const n of array) {
    if (n !== '*') {
      result.push(prev + n);
      prev = '';
    } else {
      prev = '*';
    }
  }

  return result;
}

console.log(...collapse(['*', 1, 2, '*', 3]));
console.log(...collapse(['*', '*', 2, '*', 3]));
console.log(...collapse(['*', '*', 2, '*', '*']));
console.log(...collapse(["0", "1", "2", "3", "4", "5", "6", "*", "*", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "*", "34", "*", "36", "37", "*", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "*", "54", "*", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "*", "*", "*", "*", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "*", "91", "92", "93", "94", "95", "*", "97", "*", "*"]));

基本在线基准测试
| 测试用例名称|结果|
| --------------|--------------|
| 简单循环(存储上一页)|1,228,712次操作/秒±2.22%(采样64次运行)|
| 简单回路|351,955次操作/秒±0.24%(采样67次运行)|
| 状态机|31,174次操作/秒±0.27%(采样64次运行)|

iyfamqjs

iyfamqjs2#

这可以建模为一个简单的状态机:

我们从一个空的状态值开始。当状态为空并且遇到星号时,我们移动到星号状态。当我们遇到不同的值时,我们将其附加到我们的输出并保持在空状态。当我们处于星号状态时,如果我们遇到星号,我们保持在星号状态并不处理输出。当我们遇到其他东西时,我们将*<value>附加到输出中,并返回到空状态。最后不需要清理;我们只返回输出。
我们可以用一个简单的reduce调用来编写这个状态机,它跟踪一个state变量和当前的output数组,如下所示:

const trimAsterisks = (data) => data .reduce (
  ({state, output}, val) => val == '*' 
    ? {state: '*', output} 
    : {state: '', output: output .concat (state + val)}, 
  {state: '', output: []}
) .output

const data = ["0", "1", "2", "3", "4", "5", "6", "*", "*", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "*", "34", "*", "36", "37", "*", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "*", "54", "*", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "*", "*", "*", "*", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "*", "91", "92", "93", "94", "95", "*", "97", "*", "*"]

console .log (trimAsterisks (data))
.as-console-wrapper {max-height: 100% !important; top: 0}

因为我们的状态值要么是一个星号,要么是一个空字符串,所以我们可以将它放在值的前面,然后将结果追加到输出数组中。

lvmkulzt

lvmkulzt3#

function myFunc(data) {
  let result = [];
  for (let index = 0; index < data.length - 1; index++) {
    let current = data[index];
    let next = data[index + 1];
    // if current and next create a pair like *<number>
    if (current == '*' && next != '*') {
      result.push(current + next)
    }
    // if current is not star and not pushed already
    if (current != '*' && !result.includes(`*${current}`)) {
      result.push(current);
    }
  }
  return result;
}

// Ex: 1
console.log(myFunc(["0", "1", "2", "3", "4", "5", "6", "*", "*", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "*", "34", "*", "36", "37", "*", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "*", "54", "*", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "*", "*", "*", "*", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "*", "91", "92", "93", "94", "95", "*", "97", "*", "*"]))
// Ex: 2
console.log(myFunc(["1", "*", "4", "5"]))
// Ex: 3
console.log(myFunc(["1", "*", "*", "4", "5"]))
// Ex: 4
console.log(myFunc(["1", "*", "4", "*"]))
// Ex: 5
console.log(myFunc(["1", "4", "*", "*", "*"]))

说明-

1.循环元素。
1.检查当前项和下一项的顺序,如果是*<non_star>,则合并两者并将其推入新数组。
1.如果当前项目不是一个星星,也不是已经作为*<curent_item>出现,那么也推它。

pgvzfuti

pgvzfuti4#

下面是皮尔彻的“简单循环(store prev)”解决方案的优化版本:

function collapsePrev2(array) {
  const result = [];

  let prev = false;
  for (const n of array) {
    if (n !== '*') {
      if (prev) {
         result.push('*' + n);
         prev = false;
      }
      else
        result.push(n);
    }
    else
      prev = true;
  }

  return result;
}

区别在于:

  • prev中存储布尔值而不是字符串
  • 仅在必要时连接字符串(而不是在每次迭代时)
  • 仅在必要时重置prev

该基准给出:
| 测试用例名称|结果|
| --------------|--------------|
| 折叠Prev|1,130,945次操作/秒±0.59%(采样65次运行)|
| 折叠Prev 2|1,299,316次操作/秒±0.73%(采样67次运行)|

pw9qyyiw

pw9qyyiw5#

您几乎就完成了!缺少的一个功能是测试连续的* s?

const datasets = [
  ["1", "*", "4", "5"],
  ["1", "*", "*", "4", "5"],
  ["1", "*", "4", "*"],
  ["1", "4", "*", "*", "*"],
]

const output = datasets.map(
  dataset => dataset
  .map((d, index, arr) => arr[index - 1] === '*' && d !== '*' ? '*' + d : d)
  .filter(d => d !== '*')
)

console.log(output);
qltillow

qltillow6#

至于OP的用例,我提出了一个数组join,基于regex的字符串replace和字符串split方法,其中正则表达式模式如下...

/(?:,\*)+(?:,(?<value>[^,*]+))?/g

...以及它的description is provided with the pattern's test/playground page
其优点是将提供的数组数据作为一个大字符串处理(因此join处理数组),并使用单个基于正则表达式的replace任务处理它,其中中间替换结果是split再次进入最终数组结构。

const sampleData = ["0", "1", "2", "3", "4", "5", "6", "*", "*", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "*", "34", "*", "36", "37", "*", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "*", "54", "*", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "*", "*", "*", "*", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "*", "91", "92", "93", "94", "95", "*", "97", "*", "*"];

// see ... [https://regex101.com/r/I3XdKM/1]
const regXAsteriskSequences = /(?:,\*)+(?:,(?<value>[^,*]+))?/g;

console.log(
  'array `join` and regex-based `replace` only ...',
  sampleData
    .join(',')
    .replace(regXAsteriskSequences, (_, value = null) =>
      (value !== null) && `,*${ value }` || ''
    )
);
console.log(
  'array `join`, regex-based `replace` and string `split` ...',
  sampleData
    .join(',')
    .replace(regXAsteriskSequences, (_, value = null) =>
      (value !== null) && `,*${ value }` || ''
    )
    .split(',')
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

编辑...以便提供一种***“更贴近实际”***的方法。

const sampleData = ["0", "1", "2", "3", "4", "5", "6", "*", "*", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "*", "34", "*", "36", "37", "*", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "*", "54", "*", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "*", "*", "*", "*", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "*", "91", "92", "93", "94", "95", "*", "97", "*", "*"];

function trimAsteriskSequences(arr) {
  let result = [], idx = -1, currentItem, nextItem;

  while (currentItem = arr[++idx]) {
    if (currentItem !== '*') {

      result.push(currentItem)
    } else if (
      (currentItem === '*') &&
      (nextItem = arr[idx + 1]) &&
      (nextItem !== '*')
    ) {
      result.push(['*', nextItem].join(''))
      ++idx;
    }
  }
  return result;
}

console.log(
  trimAsteriskSequences(sampleData)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

***最后一点***我自己发现基于正则表达式的变体更优雅,更有表现力,尽管上面提供的解决方案可能更容易阅读/维护初学者。

p3rjfoxz

p3rjfoxz7#

这里有一个解决方案。

let data = ["0", "7", "*", "9", "33", "34", "*", "*", "36","54", "*", "89", "*","97", "98", "*"];

let newArr = [];
let isAstrictFound = false;
data.map(val =>{
  if(val !== '*'){
    let newVal = isAstrictFound ? '*'+val.toString() : val.toString();
    newArr.push(newVal);
    isAstrictFound = false;
  }else{
    isAstrictFound = true;
  }
});
console.log(newArr);

相关问题