-4

I'm reaching out to see if there are anyone that could point me to the right direction. I have a program that produces an array of booleans that consist of either false(0) og true(1). Like this: [0,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,1,1,1,0,0,1,0]. This array is usually between 50 and 400 long. Instead of showing the whole array to the end user I want to make it more "readable". One of the things I have to do is to split in into smaller repeatable chunks of arrays that shows how many times it should repeated (1 to n times). I'll give som examples:

Example number 1 simple:

Before: [1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1] 
After: [1,0,1]*7 time, 

Example number 1 more complex:

Before: [1,1,1,0,1,1,0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1] 
After: [1,1]*1 time, [1,0,1]*4 times, [0,0] *1 time, [1,0,1]*2 times

As you can see from the examples both before and after is actually the same but only different way of printing it.

Remember that in my program I can have bigger arrays (size up to 400). And often it is much easier to print out (to the user) [1,0,1,1]* 55 than [1,0,1,1,...< long array>...,1,0,1,1]

Edit: The main goal is to make the array easier to read, since it will be read 1 by 1 (by a human).

So '01'*22 rather than '01010101010101010101010101010101010101010101'

And also I would prefer a higher multiplication number rather than a low one.

'01'*22 is better than '0101'*11 which is better than '010101'*7 + '01'*1

Christian
  • 819
  • 1
  • 8
  • 23
  • 1
    Please visit [help], take [tour] to see what and [ask]. Do some research, search for related topics on SO; if you get stuck, post a [mcve] of your attempt, noting input and expected output, preferably in a [Stacksnippet](https://blog.stackoverflow.com/2014/09/introducing-runnable-javascript-css-and-html-code-snippets/) – mplungjan May 17 '20 at 06:53
  • I don't quite understand the system by which you want to split up the array. – domsson May 17 '20 at 07:04
  • 2
    @domsson He wants to find patterns, which is why I re-opened. It is not a chunking issue, but a parser issue – mplungjan May 17 '20 at 07:05
  • So is `10101010` `4*10` or `2*1010` ? – mplungjan May 17 '20 at 08:46
  • Both is correct, but I would prefer 4*10. So the higher the multiplication number the better it is. – Christian May 17 '20 at 10:11
  • What is the maximum largest size of the sub array that you want to break the large array into. I beleive is this is np problem hence some constrains may help... – Sankalp Bhamare May 27 '20 at 10:05
  • I think it is important to clarify or verify that we're talking about consecutive copies of a pattern, and not a total count in the group. For instance for "101010 1111 101010", a valid representation would be 3*10, 2*11, 3*10 but not 6*10, 2*11. Please confirm. Spaces were included in binary string for ease of human visual inspection. ;-) – MicroservicesOnDDD May 28 '20 at 00:03
  • And is 2 the minumum group size and not 1. So for "1111", 4*1 would be invalid, right? – MicroservicesOnDDD May 28 '20 at 00:04

3 Answers3

2

This might work for most of the cases..

let largeArray = [1,1,1,0,1,1,0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1];
let maxSubSize = 5;//change this value! for chunk size.
document.getElementById("input").innerHTML = JSON.stringify(largeArray);
function patternMatch(a,b){
 if(a.length == b.length){
   let i;
   for(i=0; i< a.length;i++){
     if(a[i] != b[i])
       return false;
    }
   return true;
  } else {
    return false;
  }
}

function checkPattern(tArray,index,pattern){
 let score = 0;
 for(let i = index;i < tArray.length-pattern.length+1;i+= pattern.length){
  
     if( patternMatch(tArray.slice(i,i+pattern.length),pattern) )
         score++;
        else
         break;
    }
    return score;
}


function bakeResult(largeArray) {
  let result = [];
  for(let i = 0; i < largeArray.length;i++){
    let pattern = [];
    let bestScore = -1;
    let bestPattern;
    for(let j= i;j < i+maxSubSize;j++){
     pattern.push(largeArray[j]);
      let score = checkPattern(largeArray,j+1,pattern);
     if(score > bestScore) {
          bestPattern = pattern.slice(0);
          bestScore = score;
      }
    }

    i += (bestScore+1)*bestPattern.length-1;
    //console.log("start",tArray[i],i);
    result.push({pattern:bestPattern,count:bestScore+1})
  }
  return result;
}

function reverseResult(result){
  result = result.reverse();
  for(let i =0; i < result.length;i++){
    result[i].pattern = result[i].pattern.reverse();
  }
  return result;
}



function drawResult(result){
 for(let i= 0; i < result.length;i++){
     let tstr = "";
     let pattern = result[i].pattern;
        for(let j=0; j < pattern.length;j++){
          tstr += pattern[j]+"";
        }
       
        tstr += " - "+ result[i].count + " times";
        document.getElementById('result').innerHTML += "<button>"+tstr+"</button>"
    }
    //document.getElementById('result').innerHTML += "<hr>"
}
let result = bakeResult(largeArray);

tresult = bakeResult(largeArray.reverse());
tresult = reverseResult(tresult);
if(result.length < tresult.length){
  drawResult(result);
}else{
  drawResult(tresult);
}
body {
  background: white;
  color: #323232;
  font-weight: 300;
  height: 100vh;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-family: Helvetica neue, roboto;
}

img {
  width: 56px;
  height: 48px;
}

h1 {
  font-weight: 200;
  font-style: 26px;
  margin: 10px;
}
<div>
  <div id="input">
  </div>
  
  <h2>=</h2>
  <div id="result">

  </div>
</div>

This solution worked for the example cases...

Codepen Preview

Sankalp Bhamare
  • 523
  • 3
  • 7
  • This is exactly what I want. Thank you very much. But before I except the answer. Is it possible to predefine a maximum length of the repeatable chunk? Let say that I do not want repeatable chunks where the length of the array is over 10. – Christian May 28 '20 at 08:57
  • Christian I have updated the solution , you can now check it out! – Sankalp Bhamare May 28 '20 at 09:15
  • Want a new challenge?Try to get as few as possible chunks (that also is less than the predefined length).This input `[0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,1,1,0]` gives 19 chunks – Christian May 28 '20 at 20:55
  • Just to add to the previously comment. I testet it with maxSubSize=10 to get 19 chunks. To reduce a chunk you can typically take 2 chunks like theses: [1] - 1 times and [0] - 3 times to reduce it to [1000] - 1 time. – Christian May 28 '20 at 21:11
  • Yes, Christian this is a np problem, we can never find the perfect / best solution using some algorithm, we can only try to find the closest optimal solution, finding best solution for these problems means brute force , and performing bruteforce in js is just not possible, considering the speed, performance and capacity of javascript... – Sankalp Bhamare May 29 '20 at 03:34
1

You yould iterate the array/string from the end and store for each step the optimal solution. At last take the first item of the array.

This works with lage data as well.

function getChunks(data) {

    function same(position, size) {
        for (let i = 0; i < size; i++) {
            const right = offset + i + position * size;
            if (right >= data.length || data[offset + i] !== data[right]) return false;
        }
        return true;
    }

    function add(offset, patternSize, count) {
        let result = chunks[offset],
            temp = data.slice(offset, offset + patternSize) + '<' + count,
            product = patternSize * count,
            score = patternSize * ((count - 1) || 0.5),
            size = patternSize;

        if (offset + product < data.length) {
            let chunk = chunks[offset + product];
            score += chunk.score;
            temp += '|' + chunk.result;
            size += chunk.size;
        }

        if (!result) {
            chunks[offset] = { result: temp, score: 0.5, size };
            return;
        }
        if (result.score < score || result.score === score && size < result.size) {
            chunks[offset] = { result: temp, score, size };
        }
    }

    var chunks = [],
        offset = data.length;

    while (offset--) {
        let patternSize = data.length - offset + 1;

        while (--patternSize) {
            let count = 0;
            while (same(++count, patternSize)) add(offset, patternSize, count);
            add(offset, patternSize, count);
        }
    }
    return chunks[0].result;
}

console.log(getChunks('11101110111101111000010011110001100'));
console.log(getChunks('101101101101101101101'));
console.log(getChunks('1110110110110100101101'));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Thanks! This code does also work very well! Short and concise. – Christian May 28 '20 at 20:28
  • I compared your code with one of the other solution at this page with this input: `00000000000001000000000001000000001000000001000000010000001000000100000010000001000000100000100000010000010000010000010000010000010000100000100000100001000010000010000100001000010000100010000100010000100010001000100010010010010110` And the result was 34 chunks (the other code from @Sankalp Bhamare gave me 19 chunks). So there might be some room for improvements here =) – Christian May 28 '20 at 20:43
  • @Christian, i get 47 chunks from Sankalp Bhamare. please check. my approach has 34. – Nina Scholz May 28 '20 at 20:58
  • Sorry, my mistake Sankalp Bhamar solution let you adjust the maximum length within a chunk (maxSubSize). If you adjust this to 10 and not 5, then you'll get 19 chunks. – Christian May 28 '20 at 21:08
  • Your code has definitive potential. In the result (with 34 chunks) I get `....|1<1|0<5|1<1|0<5|1<1|0<4|1<1|0<4|1<1|0<4|....` this could easily get squashed to `... 10000<2|10000<3...` so just here you can reduce the chunk from 10 to 2 – Christian May 28 '20 at 21:21
  • So let say that max chunksize can be 10. Then you could squash it from 34 chunk down to 16 chunks just by massaging you result. Here is an example of what the end result might be `0<13|1<1|0<11|100000000<2|0<6|0100000<6|100000<1|010000<6|100000<2|10000<3|01000<5|10000<1|1000100<1|0010<5|010<3|110<1` – Christian May 28 '20 at 21:30
0

I found a way, to keep order, and make it more human-readable:

let bools = [0,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,1,1,1,0,0,1,0];
bools.push('');

let res = document.getElementById("res");

let formatted = "";
let trueOrFalse = bools[0];
let counter = 1;
let substring = "";

bools.forEach((element, index) => {
  if (trueOrFalse !== element) {
    
   if (counter === 1) {
      substring = `<span style="color: ${
        trueOrFalse === 0 ? "red" : "blue"
      }">${trueOrFalse}*1</span> `;
   }
        
    trueOrFalse = element;
    counter = 1;
    formatted += substring;
  } else {
    counter++;
    substring = `<span style="color: ${
      trueOrFalse === 0 ? "red" : "blue"
    }">${trueOrFalse}*${counter}  </span> `;
  }
});

res.innerHTML = formatted;
<div id="res">Calculating...</div>

Also, created a codepen: https://codepen.io/IDONNHAVE/pen/pojmdMm

If you have any questions regarding the code, I'll help you out.

PS: please forgive my variable namings

Laczkó Örs
  • 1,082
  • 1
  • 18
  • 38
  • 1
    The result on the codepen is incorrect. The fourth pattern is computed as `0*2` while it should be `100*3` – Andreas Dolk May 27 '20 at 09:24
  • I see, few minutes and I'll fix it – Laczkó Örs May 27 '20 at 09:44
  • It's still not grouping like the OP wanted it. You seem to count repeats of single elements and can't detect larger patterns yet – Andreas Dolk May 27 '20 at 10:07
  • You might be right. But he said "So the higher the multiplication number the better it is", if he will not like it I'll change it. – Laczkó Örs May 27 '20 at 10:12
  • He also said "Edit: The main goal is to make the array easier to read, since it will be read 1 by 1 (by a human)." I think it is pretty readable, and again, if ho does not like it, I'll change it. – Laczkó Örs May 27 '20 at 10:13
  • Thanks for your code, but unfortunately this does not give me the output I wanted. I want biggest possible repeatable chunks (where chunks.length > 1 ) – Christian May 28 '20 at 09:09