-1

I have an array containing numbers, in that array there're some numbers that occur many times in consecutive order one after the other, and I want to compress those repeated numbers into a very specific format 'Number*Times' to reduce the size of the array:

input:  [0, 1, 2, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 5, 6, 0]
         ---------^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^--------
output: [0, 1, 2,'0x3', 3, 2, '0x6', 5, 6, 0]
let array = [0, 1, 2, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 5, 6, 0];
let string = array.toString();
let string_compressed = string.replace(/(\d+,)(\1)+/g, (x) => {
  return "Number*" + x.split(",").length + ",";
});
let array_compressed = string_compressed
  .split(",")
  .map((x) => (isNaN(Number(x)) ? x : Number(x)));
console.log(array_compressed); //[0, 1, 2, 'Number*4', 3, 2, 'Number*7', 5, 6, 0]

I don't know how to get the number that repeated so I put Number instead! I used regex to solve it, I know if you think to solve problem with regex, they become two problems!
BUT Guys I'm sure this isn't the efficient way to solve this problem, And there're other ways to solve it! what do you suggest if you want to solve this problem?

XMehdi01
  • 5,538
  • 2
  • 10
  • 34
  • [This answer](https://stackoverflow.com/a/72483715/438273) to another question used a technique which would look very similar when applied to this question. You can study it and adapt it to your data types and format. – jsejcksn Jul 30 '22 at 12:43
  • It's possible without regex, too – qrsngky Jul 30 '22 at 12:50
  • @jsejcksn not the same I deal with array not string, besides the array contains number not string! – XMehdi01 Jul 30 '22 at 12:54
  • @qrsngky I think of it with regex, but could you suggest how to make it without regex! – XMehdi01 Jul 30 '22 at 12:54

3 Answers3

1

Because your regex to find how many numbers repeat already only matches numbers that are in consecutive order in your array, you can simply just take the first index of the x.split(",") array and return that.

Edit:

Also, as @qrsngky out, your x.split(",").length wasn't actually the right length, because when you split it by comma, there is a null character at the end:

let array = [0, 1, 2, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 5, 6, 0];
let string = array.toString();
let string_compressed = string.replace(/(\d+,)(\1)+/g, (x) => {
  console.log(x.split(","));
  return "Number*" + x.split(",").length + ",";
});
let array_compressed = string_compressed
  .split(",")
  .map((x) => (isNaN(Number(x)) ? x : Number(x)));
console.log(array_compressed);

Sorry for missing that, and props to the comments! I just fixed it by subtracting one from the length.

Edit 2:

For edge cases, we can just add a comma and use slice.

I attached the complete fixed code snippet below:

let array = [0, 1, 2, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 5, 6, 0, 0, 0];
let string = array.toString() + ",";
let string_compressed = string.replace(/(\d+,)(\1)+/g, (x) => {
  return x.split(",")[0] + "*" + (x.split(",").length - 1) + ",";
});
let array_compressed = string_compressed
  .slice(0, -1)
  .split(",")
  .map((x) => (isNaN(Number(x)) ? x : Number(x)));
console.log(array_compressed);
HenryGetz
  • 73
  • 6
  • 1
    Your current version outputs "0*4" (instead of "0*3") and "0*7" (instead of "0*6") – qrsngky Jul 30 '22 at 13:06
  • When `x` is `"0,0,0,"`, after the split you get `['0','0','0','']`, that's why the length is one too many. Should be `(x.split(",").length-1)` instead of just `x.split(",").length`. – qrsngky Jul 30 '22 at 13:19
  • 1
    Also, your solution didn't handle a case like `[2,0,0,0]`, the output becomes `[2, '0*2', 0 ]`. – qrsngky Jul 30 '22 at 13:23
  • Edit 2 looks fine. But in the process, you accidentally replaced part of the code from Edit 1 with 'Number*'. – qrsngky Jul 30 '22 at 13:42
  • The top code was just an example of how it was broken, the bottom one is the solution – HenryGetz Jul 30 '22 at 14:22
1

Assume your original array consists of none other than numbers.

Non-regex approach: based on a for loop and counting how many repetitions had been encountered.

let array = [0, 1, 2, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 5, 6, 0]

let buffer = [];
let currentNum = array[0], count = 1;

for (let i = 1; i < array.length; i++) {
    if (array[i] === currentNum) {
        ++count;
    } else {
        buffer.push( count === 1 ? currentNum : (currentNum + 'x' + count) );
        currentNum = array[i];
        count = 1;
    }
}
//don't forget the last number
if(currentNum !== undefined) buffer.push( count === 1 ? currentNum : (currentNum + 'x' + count) );
console.log(buffer);

The if(currentNum !== undefined) check is only useful in case it's an empty array.

qrsngky
  • 2,263
  • 2
  • 13
  • 10
1

Another example of how not to do it with string manipulation by coding what you want done:

function packArray(array) {
    var packed = [];
    for( var i = 0; i < array.length; i=j) {
        var entry = array[i];
        for(var j = i+1; array[j] === entry && j<array.length; ++j);
        packed.push( j > i+1 ? `${entry}x${j-i}` : entry);
    }
    return packed;
}

console.log( packArray([0, 1, 2, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 5, 6, 0]))

You could substitute let for var except for var j which should remain the same to allow access to j outside the nested for loop.

traktor
  • 17,588
  • 4
  • 32
  • 53
  • 1
    Interesting approach. If one uses `let j`, then it should be placed before `i=j`. So `var j` is useful here. – qrsngky Jul 30 '22 at 13:59