this is the problem description:
Given an array of integers, calculate the fractions of its elements that are positive, negative, and are zeros. Print the decimal value of each fraction on a new line.
for example given the array arr=[1,1,0,-1,-1] output should be:
0.400000
0.400000
0.200000
I know there is more more simple solution for it ,and i am sorry for my silly simple question but i wanna make my code work, my code sorts the output based on the key and removes duplicates. for this arr, my code output is:
0.200000
0.400000
thank you so much in advance for any help.
function plusMinus(arr) {
var freq = {};
for (var i = 0; i < arr.length; i++){
if (freq[arr[i]]) {
freq[arr[i]]++;
} else {
freq[arr[i]] = 1;
}
} for(var key in freq){
console.log((freq[key]/arr.length).toFixed(6));
}
}
You could take an object with predifined properties, this prevents each loop for checking the existence and take an array of keys for getting the result in a wanted order.
function plusMinus(arr) {
var freq = { 1: 0, '-1': 0, 0: 0 },
i, key;
for (i = 0; i < arr.length; i++) {
freq[arr[i]]++;
}
for (key of [1, -1, 0]) {
console.log((freq[key] / arr.length).toFixed(6));
}
}
plusMinus([1, 1, 0, -1, -1]);
Let's make sure the order of key in the map by defining it first.
function plusMinus(arr) {
var freq = {
posetive: 0,
negative: 0,
zero: 0
};
for (var i = 0; i < arr.length; i++){
if( arr[i] < 0) {
freq.negative++;
} else if(arr[i] > 0) {
freq.posetive++;
} else {
freq.zero++;
}
}
for(var key in freq){
console.log((freq[key]/arr.length).toFixed(6));
}
}
plusMinus([1,1,0,-1,-1]);
You can use reduce.
Here idea is
First loop through original array and check for the value.
If value is zero we increment count of zero key.
If value is positive we increment count of pos key.
If value is negative we increment count of neg key.
Finally we divide each count by length of array.
let arr = [1,1,0,-1,-1]
let op = arr.reduce((op,inp)=>{
if(inp === 0){
op.zero.count++
} else if (inp > 0){
op.pos.count++;
} else {
op.neg.count++;
}
return op
},{zero:{count:0},pos:{count:0},neg:{count:0}})
let final = Object.entries(op).map(([key,value])=>({
[key] : value.count / arr.length
}))
console.log(final)
Use reduce, map and filter:
const arr = [1, 1, 0, -1, -1];
const counts = arr.reduce((acc, curr) => {
if (!curr) acc[0]++;
else if (curr > 0) acc[1]++;
else acc[2]++;
return acc
}, [0, 0, 0]);
const result = counts.map(e => e / arr.length).filter((e, i, a) => a.indexOf(e) == i);
console.log(result);
You can try using Array.reduce and the resulting array will have the fraction of positive number at the '0'th index, negative at '1'st and zero at the '2'nd index.
Now if you want to control the count of the number of elements after decimal point, use Array.map at the end to transform it.
const array = [1,1,0,-1,-1];
function plusMinus(arr){
const output = arr.reduce((acc, ele) => {
if(ele > 0){
acc[0] = ((acc[0] || 0 ) + 1 / arr.length);
}
if(ele < 0){
acc[1] = ((acc[1] || 0 ) + 1 / arr.length);
}
if(ele === 0) {
acc[2] = ((acc[2] || 0 ) + 1 / arr.length);
}
return acc;
}, []).map(ele => ele.toFixed(6));
console.log(...output);
}
plusMinus(array);
Math.sign is your friend here.
Math.sign
Also lodash would really help this snippet to be cleaner, I highly recommend _.countBy. Lodash .countBy
Here's the code.
const plusMinus = (numbers) => {
// Count by Sign (-1, 0 1)
const countSign = _.countBy(numbers, Math.sign);
// _.countBy return object, of counted { '1': 2, '0': 1, '-1': 2 }
// Print them in orders
const printOrder = [1, -1, 0];
printOrder.forEach(sign => {
console.log((countSign[sign] / numbers.length).toFixed(6));
});
}
const testArr = [1,1,0,-1,-1];
plusMinus(testArr);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>
Here is another one-line solution using Array.reduce() and Array.forEach() functions:
const plusMinus = arr => arr
.reduce((res, curr) => ++res[!curr ? 2 : curr < 0 ? 1 : 0] && res, [0, 0, 0])
.forEach(i => console.log((i / arr.length).toFixed(6)));
plusMinus([1, 1, 0, -1, -1]);
Related
I'm looking to find two numbers in an array that are equal to a particular target number. I thought this would be a simple task using .filter but for some reason my code only works when I'm looking for a target number of 4 but doesn't work for anything else?
What am I missing here?
var numbers2 = [1,2,3,4];
var target = 3;
var found = numbers2.filter((num) => {
return (num + num) !== target;
});
console returns (4) [1,2,3,4] as opposed to 2[1,2].
var numbers = [1,4,3,2,6,8,12,1,1,1,2,3,4];
var target = 3;
var output = [];
// Use a set to remove duplicate numbers
numbers = [...new Set(numbers)]; // Only do this step if you dont want duplicates ( like 2+2 = 4 so if your target was for 2, would not show up in the list )
// Sort the numbers from lowest to highest
numbers.sort( (a,b) =>a-b);
// Get index of first number that matches the target or is greater than the target
let index;
for( let i =0; i < numbers.length; i++) {
if( numbers[i] >= target ) {
index = i;
break;
}
}
// Remove all numbers from the array starting at the previous index as these are not possible to add up with another number to the target
if( index ) {
numbers.splice(index, numbers.length - index );
}
// Loop through the remianing array to get first number
numbers.forEach( ( num1, index1) => {
// Loop through array again to get second number
numbers.forEach( (num2, index2) => {
// Check if number is same is same index as you dont want to add the same value to itself, then check if the 2 numbers equal the target number
if( index1 !== index2 && num1 + num2 === target ) {
// If number already exists in array dont duplicate otherwise add it to the array
if( output.indexOf( num1 ) == -1 ) {
output.push( num1);
}
// If number already exists in array dont duplicate otherwise add it to the array
if( output.indexOf( num2 ) == -1 ) {
output.push( num2);
}
}
});
});
console.log( output);
You could find the array location of your target number through using a array.forEach, array.indexOf(), array.find(), and array.findIndex():
let numbers2 = [1,2,3,4];
let target = 4;
//Using foreach
numbers2.forEach((item, index)=>{
if (item == target){
console.log("Found the target at array location "+index);
}
});
//Or through using indexOf():
console.log("Found the target at array location "+numbers2.indexOf(target));
//Or through using find():
const found = numbers2.find(element => element == target);
console.log("Found "+target+" in the array.");
//Or through findIndex():
const target1 = (a) => a == target;
console.log("Found the target at array location "+numbers2.findIndex(target1));
Assuming:
you only need one pair
[2,2] does not count when your target is 4 (as '2' only appears once in the array)
One way to go is:
let numbers = [1, 2, 3, 4]
let target = 4;
let output = [];
const N = numbers.length
outer: for (let i = 0; i < N; i++) {
for (let j = i + 1; j < N; j++) {
if (numbers[i] + numbers[j] === target) {
output.push(numbers[i], numbers[j])
break outer;
}
}
}
console.log(output); //[1,3]
Edit: even if you want more than one pair, it's easy to modify to get that effect (now the target is 5):
let numbers = [1, 2, 3, 4]
let target = 5;
let output = [];
const N = numbers.length
for (let i = 0; i < N; i++) {
for (let j = i + 1; j < N; j++) {
if (numbers[i] + numbers[j] === target) {
output.push([numbers[i], numbers[j]])
}
}
}
console.log(output); //[[1,4], [2,3]]
This is an ideal case for the humble for loop. Methods like .forEach() will always try to loop over all the elements in an array, but if we order the data before we start the search we can break early and eliminate a lot of searching.
Ergo...
var numbers = [1,2,3,4];
var target = 5;
var output = [];
// Handling ordered data is much faster than random data so we'll do this first
numbers.sort();
// We want to start the inner search part way up the array, and we also want
// the option to break so use conventional for loops.
for (let i = 0; i<numbers.length; i++) {
for (let j=i+1; j<numbers.length;j++) {
// If the total = target, store the numbers and break the inner loop since later numbers
// will all be too large
if ((numbers[i]+numbers[j])===target) {
output.push([numbers[i], numbers[j]]);
break;
}
}
// Stop searching the first loop once we reach halfway, since any subsequent result
// will already have been found.
if (numbers[i]>(target/2)) {
break;
}
}
console.log( output);
It makes very little sense to get an array of single numbers, because you'll get all the numbers except for the last one unless the array starts at zero or there are numbers skipped. So I've written a function that'll return an array of single numbers or an array of expressions (strings).
First, make a copy of the array:
const array = [1, 2, 3, 4]
const copy = array.slice(0);
Next, use .flatMap() for the first set of iterations:
array.flatMap(num => { // This is the outer loop of numbers
If the third parameter expression is undefined it will default to false. Then .filter() the copy array, the criteria being that the number from the outer loop plus the current number of the inner loop equals the target number AND the numbers cannot be identical.
copy.filter(n => n !== num && target === n + num);
/*
Iterations on the first iteration of outer loop
1 + 1, 1 + 2, 1 + 3,...
*/
If expression is true, then use .flatMap() to return an expression (string) of whatever equals the target number or an empty array (which returns as nothing since .flatMap() flattens it's returns by a level). If both numbers are identical an empty array will be returned.
copy.flatMap(n => n === num ? [] :
target === n + num ? `${n} + ${num}` :
[]
);
If expression is true half of the array is returned so that there isn't any reversed dupes (ex. 6+2 and 2+6)
let half = result.length / 2;
result = result.slice(0, half);
const log = data => console.log(JSON.stringify(data));
// [1, 2, 3,...10]
const array10 = [...new Array(10)].map((_, i) => i + 1);
// [0, 2, 4, 6,...18]
const arrayEven = [...new Array(10)].map((_, i) => i * 2);
function operands(array, target, expression = false) {
const copy = array.slice(0);
let result = array.flatMap(num => {
if (expression) {
return copy.flatMap((n, i) =>
num === n ? [] :
target === n + num ? `${n} + ${num}` :
[]
);
}
return copy.filter(n => n !== num && target === n + num);
});
if (expression) {
let half = result.length / 2;
result = result.slice(0, half);
}
return result;
}
// Return as an array of single numbers
log(array10);
log('3: '+operands(array10, 3));
log('8: '+operands(array10, 8));
log('5: '+operands(array10, 5));
log(arrayEven);
log('2: '+operands(arrayEven, 2));
log('8: '+operands(arrayEven, 8));
log('15: '+operands(arrayEven, 15));
log('=======================');
// Return as an array of expressions (string)
log(array10);
log('3: '+operands(array10, 3, true));
log('8: '+operands(array10, 8, true));
log('5: '+operands(array10, 5, true));
log(arrayEven);
log('2: '+operands(arrayEven, 2, true));
log('8: '+operands(arrayEven, 8, true));
log('15: '+operands(arrayEven, 15, true));
I am trying to arrange any kind of array input into a sorted array that also combines the equal pairs into an array inside the same array.
I do the following
const arrangeTheArray=(arr)=>
{
//checking input here
if(arr.length<2)
{
return arr;
} else {
//sorting the array
arr= arr.sort();
}
//displaying the sorted array
console.log(arr);
for(let i=1; i<arr.length;i++)
{
for(let j=0;j<i;j++)
{
//here I am looping and comparing the values of array
if(arr[j]===arr[i]){
//putting the value
arr[j]= [arr[i],arr[j]];
}
}
}
//displaying the final output
console.log(arr);
}
arrangeTheArray([0,2,2,1,1,6,3,1,0])
e.g array input : [0,2,2,1,1,6,3,1,0]
final out put: [[0,0],[1,1,1],[2,2],3,6]
You can use reduce and map.
Here idea is
First create a object with each digit as key and group the values by key.
Now map on the grouped data, if the length of element greater than one pass element as it is, else pass the 0th index value
let combine = (arr) =>{
let groups = arr.reduce((op,inp)=>{
op[inp] = op[inp] || []
op[inp].push(inp)
return op
},{})
let final = Object.values(groups).map(e=> e.length > 1 ? e : e[0])
return final
}
console.log(combine([0,2,2,1,1,6,3,1,0]))
const arrangeTheArray=(arr)=>{
if(arr.length<2)
{
return arr;
} else {
arr = arr.sort((a,b)=>a-b);
}
let final = []
for(let i=0;i<arr.length;i++){
let current = arr[i]
let j = i;
let temp = []
while(arr[j] === current){
temp.push(arr[j])
j++
}
i = j-1
temp = temp.length > 1 ? temp : temp[0]
final.push(temp)
}
console.log(final)
}
arrangeTheArray([0,2,2,1,1,6,3,1,0])
You can use Array.reduce to accumulate the common elements in an object.
Then use Object.values and Array.from to process the nested arrays into arrays of common elements and distinct elements:
const arrangeTheArray = (arr) => {
if (!Array.isArray(arr) && arr.length < 2){
return arr;
}
const pairs = arr.reduce((acc, ele) => {
if(acc[ele]){
acc[ele].push(ele);
}else{
acc[ele] = [ele];
}
return acc;
}, {});
return Array.from(Object.values(pairs), ele => ele.length > 1 ? ele : +ele.join())
}
console.log(arrangeTheArray([0,2,2,1,1,6,3,1,0]));
You could sort the array with a callback for numbers and reduce the array by checking the predecessor p and the actual value v and push either the value or an array of the last group and the value.
function arrangeTheArray(array) {
return array
.sort((a, b) => a - b)
.reduce(
(r, v, i, { [i - 1]: p }) => r.concat([p !== v ? v : [].concat(r.pop(), v)]),
[]
);
}
console.log(arrangeTheArray([0, 2, 2, 1, 1, 6, 3, 1, 0]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
With a classsic while loop from the end, because the array shrinks.
function arrangeTheArray(array) {
var i = array.length - 1;
array.sort((a, b) => a - b);
while (i--) {
if (array[i] === (Array.isArray(array[i + 1]) ? array[i + 1][0] : array[i + 1])) {
array[i] = [].concat(array[i], ...array.splice(i + 1, 1));
}
}
return array;
}
console.log(arrangeTheArray([0, 2, 2, 1, 1, 6, 3, 1, 0]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have attended a technical interview for a development company. They asked me the following:
Giving an array of numbers (n) find 2 numbers that sum gives (k) and print them.
e.g
Input: n = [2,6,4,5,7,1], k = 8
Output: result=(2,6),(7,1)
My solution:
function findSum(n,k){
let aux = []
for (let i = 0; i < n.length; i++) {
for (let j = i+1; j < n.length; j++) {
if (n[i] + n[j] == k) {
aux.push({ first: n[i], second: n[j] })
}
}
}
return aux;
}
They told me that, it is possible to make the exercise with some kind of key or mapping.
Does some one know how to do it with only one loop?
The key to solving a question like this with low time complexity is the ability to efficiently search the data structure. A lot of answers rearrange the array in a way where searching an array is optimized. Another approach is with a data structure that inherently has fast search.
Set and Map data structures have O(1) time complexity for searches, which make them good data structures where searching can be leveraged to increase performance.
I use a new Map and traverse the array while adding it as a key. I set the key to the number and the value to the number of times I see it. I use a map over a new Set because I can also keep track of the number of instances of that particular number.
I search for the number that would sum up to k, which is: (k - num). If I find that number, I add both numbers to my results data structure and decrement the value by 1, to show that it's been used.
Time complexity: O(n), memory complexity: O(2n). Twice the amount of space compared to the original array because I have a key and a value to store in my Map
function pairSums(arr, k){
const map = new Map
const matches = []
for (let num of arr) {
const search = k - num
if (map.get(search) > 0) {
matches.push([num, k - num])
map.set(search, map.get(search) - 1)
} else if (!map.has(num)){
map.set(num, 1)
} else {
map.set(num, map.get(num) + 1)
}
}
return matches
}
console.log(pairSums([2, 6, 6, 6, 2, 4, 4, 4, 5, 7, 1, 4, 2], 8))
Match a number x from array with a key Math.min(x, k - x). Then run through your array and store every number in a hash using mentioned key. When the key you are going to add already is in the hash - check if stored value and current number gives required sum.
function findSum(n, k){
let hash = {};
for(let i = 0; i < n.length; ++i){
let x = n[i], key = Math.min(x, k - x);
if((key in hash) && hash[key] + x == k)
return [hash[key], x];
else hash[key] = x;
}
}
A task like this can be as simple or as complicated as you want to make it. Here's one solution, for example:
function findPairs(n, k) {
return n.reduce((pairs, next, i) => pairs.concat(
n.slice(i + 1)
.filter(num => next + num === k)
.map(num => [ next, num ])
),
[]
);
}
For the inputs [2, 6, 4, 5, 7, 1] and 8 will output [ [2, 6], [7, 1] ].
From https://www.geeksforgeeks.org/write-a-c-program-that-given-a-set-a-of-n-numbers-and-another-number-x-determines-whether-or-not-there-exist-two-elements-in-s-whose-sum-is-exactly-x/:
Sort the array in non-decreasing order.
Initialize two index variables to find the candidate elements in the sorted array. Initialize l to the leftmost index: l = 0, Initialize r to the rightmost index: r = n.length - 1
Loop while l < r.
if (n[l] + n[r] == k) then return 1
else if( n[l] + n[r] < k ) then l++
else r--
No candidates in whole array - return 0
I think by sorting you can do that
var n = [2,6,4,5,7,1];
var k = 8 ;
n.sort();
let start = 0, end = n.length-1;
while(start < n.length && end >= 0) {
let current_sum = (n[start] + n[end]);
if(current_sum == k) {
console.log('Found sum with '+ n[start] +' and '+ n[end]);
break;
}
else if(current_sum > k) {
end--;
} else {
start++;
}
}
if(start == n.length || end < 0) {
console.log('Not Found');
}
but while writing this code I got one another approach also
const set = new Set([2,6,4,5,7,1]);
var k = 8;
let found = false;
for (let item of set) {
let another = k - item;
if(set.has(another)){
console.log('found with '+item +' and ' +another);
found = true;
break;
}
}
if(!found) {
console.log('Not found');
}
If numbers are non-negative and the target value is within JavaScript array limit:
function findsums(arr,k){
var ret=[];
var aux=[];
arr.forEach(function(i){
if(i<=k){
if(aux[k-i])
ret.push([k-i,i]);
aux[i]=true;
}
});
return ret;
}
console.log(findsums([2,6,4,5,7,1],8));
Similar approach could work with a bitfield (or even with a sparse array of bitfields) too.
Minified alternative similar to #Andrew's great answer, but assumes that all numbers are above 0 :
var pairs = (arr, k) => arr.reduce((a, n) =>
(a[n - k]-- ? a.push([n, k - n]) : a[-n] = a[-n] | 0 + 1, a), []);
console.log(JSON.stringify( pairs([2,6,4,5,7,1], 8) ));
I have an array as below. How can previous value be copied to the array when zero occurs.
var array_numbers = [9,0,1,0,2,0,3];
var Result_array = [9,9,1,1,2,2,3];
i am able to get filter out all zeros with below code, but need to copy the previous values in the array
arr.filter(Boolean)
This is a great use of the .map() function, because every input needs an output, even if it changes.
var result_array = array_numbers.map(function(val, index, array) {
// the OR operator will move on if val is falsy... like zero
return val || array[index - 1];
});
I simplified the check, but it doesn't handle some scenarios, like if the first value is zero, or other falsy values.
Since multiple consecutive zeros are possible, one option is to hold the last non-zero value in state:
const numbers = [9, 0, 1, 0, 2, 0, 0, 3];
let last;
const result = numbers.map(n => last = n || last);
console.log(result);
Another option is to implement a “scan” that works like reduce but keeps every value produced:
const scanl1 = (array, f) => {
if (array.length === 0) {
return [];
}
let x = array[0];
const result = [x];
for (let i = 1; i < array.length; i++) {
x = f(x, array[i]);
result.push(x);
}
return result;
};
const numbers = [9, 0, 1, 0, 2, 0, 0, 3];
const result = scanl1(numbers, (m, n) => n || m);
console.log(result);
You can use .map instead of .filter, if the current index is greater than 0 you return the previous index, otherwise the current value. Not sure what you want to do with [0, 0, 0]...
const input = [9,0,1,0,2,0,3]
const output = input.map((item, index, arr) => {
if (item === 0 && index > 0) {
return arr[index-1]
}
return item
})
console.log(output)
You don't want to filter it, since you're trying to end up with an array of the same length.
What you should do, is iterate the array, and grab the item at the previous index when a 0 is found. You can use || to get the last array item for the first member if needed.
var array_numbers = [9,0,1,0,2,0,3];
var result = array_numbers.map(function(n, i, arr) {
return n === 0 ? arr[(i || arr.length)-1] : n;
});
console.log(result);
If both the first and last are 0, the first won't get changed.
So I have a series of arrays, each of which are 2500 long, and I need to serialize and store all them in very limited space.
Since I have many duplicates, I wanted to cut them down to something like below.
[0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0]
// to
[0x4,2,7,3x2,0x9]
I wrote a couple one-liners (utilising Lodash' _.repeat) to convert to and from this pattern, however converting to doesn't seem to work in most/all cases.
let serialized = array.toString().replace(/((?:(\d)+,?)((?:\2+,?){2,}))/g, (m, p1, p2) => p2 + 'x' + m.replace(/,/g, '').length);
let parsed = serialized.replace(/(\d+)x(\d+),?/g, (z, p1, p2) => _.repeat(p1 + ',', +p2)).split(',');
I don't know why it doesn't work. It may be due to some of the numbers in the array. Eye-balling, the largest one is 4294967295, however well over 90% is just 0.
What am I missing in my RegEx that's preventing it from working correctly? Is there a simpler way that I'm too blind to see?
I'm fairly confident with converting it back from the serialized state, just need a hand getting it to the state.
Straight forward and simple serialization:
let serialize = arr => {
const elements = [];
const counts = []
let last = undefined;
[0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0].forEach((el,i,arr)=>{
if (el!==last) {
elements.push(el);
counts.push(1);
} else {
counts[counts.length-1]++;
}
last = el;
})
return elements.map((a,i)=>counts[i]>1?`${a}x${counts[i]}`:a).join(",");
};
console.log(serialize([0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0]));
UPDATE
Pure functional serialize one:
let serialize = arr => arr
.reduce((memo, element, i) => {
if (element !== arr[i - 1]) {
memo.push({count: 1, element});
} else {
memo[memo.length - 1].count++;
}
return memo;
},[])
.map(({count, element}) => count > 1 ? `${count}x${element}` : element)
.join(",");
console.log(serialize([0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0]));
Pure functional deserialize:
const deserialize = str => str
.split(",")
.map(c => c.split("x").reverse())
.reduce((memo, [el, count = 1]) => memo.concat(Array(+count).fill(+el)), []);
console.log(deserialize("4x0,2,7,2x3,9x0"))
In order to avoid using .reverse() in this logic, I'd recommend to change serialization from 4x0 to 0x4
Try this
var arr = [0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0];
var finalArray = []; //array into which count of values will go
var currentValue = ""; //current value for comparison
var tmpArr = []; //temporary array to hold values
arr.forEach( function( val, index ){
if ( val != currentValue && currentValue !== "" )
{
finalArray.push( tmpArr.length + "x" + tmpArr[0] );
tmpArr = [];
}
tmpArr.push(val);
currentValue = val;
});
finalArray.push( tmpArr.length + "x" + tmpArr[0] );
console.log(finalArray);
Another version without temporary array
var arr = [0, 0, 0, 0, 2, 7, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var finalArray = []; //array into which count of values will go
var tmpCount = 0; //temporary variable to hold count
arr.forEach(function(val, index) {
if ( (val != arr[ index - 1 ] && index !== 0 ) )
{
finalArray.push(tmpCount + "x" + arr[ index - 1 ] );
tmpCount = 0;
}
tmpCount++;
if ( index == arr.length - 1 )
{
finalArray.push(tmpCount + "x" + arr[ index - 1 ] );
}
});
console.log(finalArray);
Do not use RegEx. Just use regular logic. I recommend array.reduce for this job.
const arr1 = [0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0]
const arr2 = ['0x4','2','7','3x2','0x9'];
const compact = arr => {
const info = arr.reduce((c, v) =>{
if(c.prevValue !== v){
c.order.push(v);
c.count[v] = 1;
c.prevCount = 1;
c.prevValue = v;
} else {
c.prevCount = c.prevCount + 1;
c.count[v] = c.count[v] + 1;
};
return c;
},{
prevValue: null,
prevCount: 0,
count: {},
order: []
});
return info.order.map(v => info.count[v] > 1 ? `${v}x${info.count[v]}` : `${v}`);
}
const expand = arr => {
return arr.reduce((c, v) => {
const split = v.split('x');
const value = +split[0];
const count = +split[1] || 1;
Array.prototype.push.apply(c, Array(count).fill(value));
return c;
}, []);
}
console.log(compact(arr1));
console.log(expand(arr2));
This is a typical reducing job. Here is your compress function done in just O(n) time..
var arr = [0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0],
compress = a => a.reduce((r,e,i,a) => e === a[i-1] ? (r[r.length-1][1]++,r) : (r.push([e,1]) ,r),[]);
console.log(JSON.stringify(compress(arr)));
since the motivation here is to reduce the size of the stored arrays, consider using something like gzip-js to compress your data.