How to reorder an Array to avoid consecutive duplicates - javascript

I have an array of numbers [1,2,2,2,3,4,5]
What I want to do is to sort the array to avoid equal numbers consecutively [1,2,3,2,4,2,5]. but I don't want to delete any number from the array, in the worse case when the duplicated numbers are more than the other numbers I would like to put those that are impossible to merge in other arrays.
const original = [1,2,2,2,2,2,2,3,4];
const merged = [];
const rest = [];
For example [1,2,2,2,2,2,2,3,4] the result expected should be [2,1,2,3,2,4,2] and another array with [2,2] in the original array I have 6 times 2 and I was able to merge 4 of the six in the rest
const original = [1,2,2,2,2,2,2,3,4];
const merged = [2,1,2,3,2,4,2];
const rest = [2,2];
I was trying to play with this algorithm but there is no way to get the respected result, this algorithm deletes the duplicates
const testArr = [1, 1, 2, 2, 3, 3, 1, 1, 1];
const compress = (arr, len = 0, canDelete = false) => {
if(len < arr.length){
if(canDelete){
arr.splice(len, 1);
len--;
}
return compress(arr, len+1, arr[len] === arr[len+1])
};
return;
};
compress(testArr);
console.log(testArr);
Any Idea on what is the best way to face this kind of problem?

It was a very interesting one, here is my solution:
const original = [1,2,2,2,2,3,3,2,2,2,3,4]
console.log("Before: ", original)
let object_keys = {}
//Step 1: map all the values and how many times it duplicated
for(let val of original){
if(object_keys[val]){
object_keys[val]++
}else{
object_keys[val] = 1
}
}
console.log(object_keys) // { '1': 1, '2': 7, '3': 3, '4': 1 }
//Step 2: Run over the object and create a new array. the method is each iterate, append new key
const get_the_most_duplicated_val = (object_keys, except) => {
let max = 0
let key = ''
for(let v in object_keys){
if(object_keys[v] > max && !except.includes(v)){
max = object_keys[v]
key = v
}
}
return key
}
let merged = []
let rest = []
let merged_is_completed = false
while(!merged_is_completed){
const append_val = (key) => {
merged.push(key)
object_keys[key]--
if(object_keys[key] == 0){
delete object_keys[key]
}
}
const last_val = () => {
return merged[merged.length - 1]
}
let most_key = get_the_most_duplicated_val(object_keys, [])
append_val(most_key)
let most_key2 = get_the_most_duplicated_val(object_keys, [most_key])
append_val(most_key2)
if(Object.keys(object_keys).length == 1){
if(last_val() != Object.keys(object_keys)[0]){
append_val(Object.keys(object_keys)[0])
}
for(let i=0;i<object_keys[Object.keys(object_keys)[0]];i++){
rest.push(Object.keys(object_keys)[0])
}
merged_is_completed = true
}
}
console.log("Merged: ", merged)
console.log("Rest: ", rest)
I put it in Codepen so you can test it by yourself :)
https://codepen.io/gofmannir/pen/vYXEmPa?editors=0011

Was an interesting Task, I just wrote a little code it doesn't work perfectly but you can improve it.
const insert = (el, result) => {
if (el !== result.merged[result.merged.length - 1]) {
result.merged.push(el);
if (result.rest.length !== 0) {
for (let i = 0; i < result.rest.length; i++) {
let restItemToInsert = result.rest[i];
if (result.merged[result.merged.length - 1] !== restItemToInsert) {
result.merged.push(restItemToInsert);
result.rest.splice(i, 1);
}
}
}
} else {
result.rest.push(el);
}
return result;
}
const recursive = (arr) => {
let el = arr.shift();
return arr.length === 0 ? { rest: [], merged: [el]} : insert(el, recursive(arr));
}
const testArr = [1, 1, 2, 2, 3, 3, 1, 1, 1];
const testArr2 = [1, 1, 1, 1, 3, 3, 1, 1, 1];
const original = [1,2,2,2,2,2,2,3,4];
console.log(recursive(original));
console.log(recursive(testArr));
console.log(recursive(testArr2));
If you run the code, you should get something like this.
{ rest: [ 2, 2, 2, 2 ], merged: [ 4, 3, 2, 1, 2 ] }
{ rest: [], merged: [
1, 3, 1, 3, 1,
2, 1, 2, 1
] }
{ rest: [ 1, 1, 1, 1 ], merged: [ 1, 3, 1, 3, 1 ] }
You can see, for your const original = [1,2,2,2,2,2,2,3,4]; it doesn't give the right answer. But you can improve it and make it work.
UPDATE
I wrote a better insert function, this should work for any array from your example.
const insert = (el, result) => {
result.rest.unshift(el);
for (let i = 0; i < result.rest.length; i++) {
let restItem = result.rest[i];
for (let j = 0; j < result.merged.length; j++) {
let mergedItem = result.merged[j];
if(mergedItem !== restItem) {
if (result.merged[result.merged.length - 1] !== restItem) {
result.merged.push(restItem);
result.rest.splice(i, 1);
i = -1;
break;
}
if(result.merged[0] !== restItem) {
result.merged.unshift(restItem);
result.rest.splice(i, 1);
i = -1;
break;
}
if (result.merged[j + 1] && result.merged[j + 1] !== restItem) {
result.merged.splice(j + 1, 0, restItem);
result.rest.splice(i, 1);
i = -1;
break;
}
}
}
}
return result;
}

Related

How do I split an array into odd and even numbers and place them into a matrix. What I did:

function evenAndOdd(array1){
const array1 = [1,2,3,4,5,6]
const odd = []
const even = []
for (let i=0; i<array1.length; i++){
if (i%2===0){
even.push(array1[i]);
}else {
odd.push(array1[i])
}
array1.push(odd)
array1.unshift(even)
}
return array1
}
This is what i got from my console log
/usercode/index.js:3
const array1 = [1,2,3,4,5,6]
^
SyntaxError: Identifier 'array1' has already been declared
I was expecting: array1 = [[2,4,6],[1,3,5]]
You can Also try this.
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const matrix = splitOddEven(arr);
console.log("Odd numbers:", matrix[0]);
console.log("Even numbers:", matrix[1]);
function splitOddEven(arr) {
let odd = [];
let even = [];
for (let number of arr) {
if (number % 2 === 0) {
even.push(number);
} else {
odd.push(number);
}
}
return [odd, even];
}
The error is correct, you're defining array as input to your function as well as in the function body. It should be used like this:
const array1 = [1, 2, 3, 4, 5, 6]
const result = evenAndOdd(array1)
Next, your function modifies its input and returns it. I'm guessing you just want to get the result without changing the input. So we change it like so:
function evenAndOdd(array1) {
const odd = []
const even = []
for (let i = 0; i < array1.length; i++) {
if (i % 2 === 0) {
even.push(array1[i]);
} else {
odd.push(array1[i])
}
}
return [even, odd]
}
This runs, but the result is wrong. The output is [ [ 1, 3, 5 ], [ 2, 4, 6 ] ]. The reason is that you're checking whether the index is odd/even, but the check should be on the value in the input array. You can fix it like this:
if (array1[i] % 2 === 0) {
even.push(array1[i]);
} else {
odd.push(array1[i])
}
I would also change the name array1 to input just to make it a bit more obvious what the variable represents.
Putting everything together, the code should look like this:
function evenAndOdd(input) {
const odd = []
const even = []
for (let i = 0; i < input.length; i++) {
if (input[i] % 2 === 0) {
even.push(input[i]);
} else {
odd.push(input[i])
}
}
return [even, odd]
}
const input = [1, 2, 3, 4, 5, 6]
let result = evenAndOdd(input)
console.log(result)
As a bonus, I would rather write it using filters on the input instead of a for loop and messing with indexes
function evenAndOdd(input) {
const odd = input.filter(x => x % 2 !== 0)
const even = input.filter(x => x % 2 === 0)
return [even, odd]
}
There are some logical issues in your code:
You are redefining the input to your function
You are appending the odd and even arrays to the input in each iteration
console.log(evenAndOdd([1,2,3,4,5,6]));
function evenAndOdd(array1){
const odd = [];
const even = [];
for (let i=0; i<array1.length; i++){
if (i%2===0) {
even.push(array1[i]);
} else {
odd.push(array1[i])
}
}
return [odd, even];
}

Sorting an Array that consists of numbers, BUT can include strings

I am facing an issue where I get results from API(mainly array of numbers), but if the devs make mistake and leave the field empty I will get empty string ("").
I am trying to sort this array in an ascending order and move the empty strings in the back of the Array, like that:
let arr = [3, 4, "", 1, 5, 2] // Example Array from api
This array, when modified should become:
let res = [1, 2, 3, 4, 5, ""]
I tried using the arr.sort() method, but the results look like that:
let res = ["",1 ,2 ,3 ,4 ,5]
For some reason when the element is string, the sort method puts it in the front, not in the end like it does with undefined or null for example.
Method 1
let arr = [3, 4, "", 1, 5, 2];
const res = arr.sort((a, b) => {
if (typeof a === 'string') {
return 1;
} else if (typeof b === 'string') {
return -1;
} else {
return a - b;
}
}
);
console.log(res)
Output:
[ 1, 2, 3, 4, 5, '' ]
Method 2
const res = (arr) => {
let newArr = [];
let strArr = [];
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] === 'string') {
strArr.push(arr[i]);
} else {
newArr.push(arr[i]);
}
}
return newArr.concat(strArr);
}
console.log(res(arr));

How to return all items in array that don't have their opposite?

I have for example this array [-3, 1, 2, 3, -1, 4, -2], and I'd like to return 4 because it doesn't have its own opposite. I've been struggling for several hours to understand how to implement the algorithm.
This is what I've done so far:
let numbers = [-3, 1, 2, 3, -1, 4, -2];
let sortedNumebrs = [];
//It returns the numbers array sorted
function sortedArray() {
sortedNumebrs = numbers.sort((a, b) => a - b);
return sortedNumebrs;
}
// It return an array with all positive numbers
function positive() {
let positive = sortedArray().filter((e) => Math.sign(e) === 1);
return positive;
}
// It return an array with all negative numbers
function negative() {
let negative = sortedNumebrs.filter((e) => Math.sign(e) === -1);
negative = negative.sort((a, b) => a + b);
return negative;
}
// It returns the array longer
function minLength() {
let minNum = Math.min(positive().length, negative().length);
if (minNum === positive().length) {
return positive()
} else {
eturn negative();
}
}
// It returns the array shorter
function maxLength() {
let maxNum = Math.max(positive().length, negative().length);
if (maxNum === positive().length) {
return positive()
} else {
return negative();
}
}
// Function that should return string if each numbers has its
// own opposite otherwise 4
function opposite() {
let result = (minLength() === maxLength()) ? true : false;
if (result) {
return 'Each element has own opposite';
} else {
// some code
}
}
You can try something like this:
const yourArray =[-3, 1, 2, 3, -1, 4, -2];
const result = [];
for (let el1 of yourArray) {
let hasOpposite = false;
for (let el2 of yourArray) {
if (el1 === -el2) {
hasOpposite = true;
break;
}
}
if (!hasOpposite) {
result.push(el1);
}
}
console.log(result); // [4]
or using array functions:
const yourArray = [-3, 1, 2, 3, -1, 4, -2];
const itemsWithoutOpposite = yourArray.filter(el1 => !yourArray.includes(-el1));
Here's another approach. This one assumes that duplicates need matching too, so if I have [1, -1, 1] that final 1 should have an additional -1 to match, rather than using the -1 that was matched by the initial 1. It will also not match an element with itself, so if you have a 0 you will need another zero to match.
const yourArr = [-3, 1, 2, 3, -1, 4, -2];
const result = [...yourArr];
for (let i = 0; i < result.length; i++) {
let el = result[i];
let inverse = result.indexOf(-el, i + 1);
if (inverse !== -1) {
result.splice(inverse, 1);
result.splice(i, 1);
i--;
}
}
console.log(result);

I am doing this challenge at Leet Code where you should sort array and delete all zeros and it always keeps one zero?

nums1 = [1,2,3,0,0,0]
nums2 = [2,5,6]
var merge = function(nums1,nums2) {
let deletezeros = function(numi){
for(let i = 0; i < numi.length; i++){
if(numi[i] == 0){
numi.splice(i, 1)
}
}
}
deletezeros(nums1)
deletezeros(nums2)
let result = nums1.concat(nums2)
result.sort()
return result
};
let a = merge(nums1,nums2)
console.log(a)
result is
[
0, 1, 2, 2,
3, 5, 6
]
Decrement i after calling splice:
nums1 = [1, 2, 3, 0, 0, 0]
nums2 = [2, 5, 6]
var merge = function(nums1, nums2) {
let deletezeros = function(numi) {
for (let i = 0; i < numi.length; i++) {
if (numi[i] == 0) {
numi.splice(i, 1)
i--;
}
}
}
deletezeros(nums1)
deletezeros(nums2)
let result = nums1.concat(nums2)
result.sort()
return result
};
let a = merge(nums1, nums2)
console.log(a)

Recursion is skipping values

I'm trying to assign/place a set of numbers randomly within a new array as a pair: [1,2,3,4,5,6,7,8] should equal [[1,1],[8,8],[3,3],[7,7],[2,2],[4,4],[5,5],[6,6]]
let numbers = [1,2,3,4,5,6,7,8]
let arrayToBeFilled = [];
function assign(num) {
let randomNumber = Number(Math.floor((Math.random() * 8)));
if(arrayToBeFilled[randomNumber] == null ) {
arrayToBeFilled[randomNumber] = [num, num] ;
} else if (arrayToBeFilled[randomNumber] == Array) {
return assign(num);
} else {
console.log('Trying a new number');
}
}
for (num in numbers) {
assign(Number(num));
}
console.log(arrayToBeFilled);
return arrayToBeFilled;
Returns the array but with values missing where the recursion should have filled the array (what I'm expecting at least). See <1 empty item>.
Trying a new number
Trying a new number
Trying a new number
[ [ 0, 0 ], [ 7, 7 ], [ 5, 5 ], <1 empty item>, [ 2, 2 ], [ 1, 1 ] ]
Anyone have any idea why this is happening??
I made some edits to your code:
/* prefer functions instead of global variables */
function main() {
let numbers = [1, 2, 3, 4, 5, 6, 7, 8]
let arrayToBeFilled = [];
for (num of numbers) { /* Use 'for...of' syntax for array iteration */
assign(Number(num), arrayToBeFilled);
}
return arrayToBeFilled
}
function assign(num, arr) {
const randomNumber = Number(Math.floor((Math.random() * 8)));
if (arr[randomNumber] == null) {
arr[randomNumber] = [num, num];
} else if (Array.isArray(arr[randomNumber])) { /* Proper way to check if element is an Array type */
return assign(num, arr);
} else {
return []
}
}
console.log(main());
Here's my take. The beauty of this is of course the abstraction in form of the shuffle function which works on all arrays, and can be put away into a utility sub file.
function shuffle(a) {
// you can replace this with "let n = a" if you don't care about
// the incoming array being altered
let n = [...a];
for (let i = n.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[n[i], n[j]] = [n[j], n[i]];
}
return n;
}
let numbers = [1,2,3,4,5,6,7,8];
console.log( shuffle( numbers ).map( n => [n,n] ) );
You could create function that will randomize elements and return array of the nth length for each element using while loop.
let numbers = [1, 2, 3, 4, 5, 6, 7, 8]
function randomize(data, n) {
const result = [];
data = data.slice();
while (data.length) {
const pos = Math.floor(Math.random() * data.length);
const el = data.splice(pos, 1).pop();
result.push(Array.from(Array(n), () => el));
}
return result;
}
console.log(randomize(numbers, 2))
console.log(randomize(numbers, 4))
Try the following:
function assign(num) {
let randomNumber = Number(Math.floor((Math.random() * 8)));
if(arrayToBeFilled[randomNumber] == null ) {
arrayToBeFilled[randomNumber] = [num + 1, num + 1] ;
} else {
assign(num);
}
}
You had an else in the code which was skipping one place in the array to be filled
NON-REPEATING random numbers (https://jsfiddle.net/th3vecmg/2/)
let numbers = [1, 2, 3, 4, 5, 6, 7, 8]
let arrayToBeFilled = [];
function assign(numbers, i, size) {
if (i < size) {
assign(numbers, ++i, size);
}
var randomNumber = numbers[Math.floor(Math.random() * numbers.length)];
arrayToBeFilled.push([randomNumber, randomNumber]);
numbers.splice(numbers.indexOf(randomNumber), 1);
}
assign(numbers, 0, numbers.length - 1)
console.log(arrayToBeFilled);
REPEATING random numbers (https://jsfiddle.net/th3vecmg/3/)
let numbers = [1, 2, 3, 4, 5, 6, 7, 8]
let arrayToBeFilled = [];
function assign(numbers, i) {
let randomNumber = Number(Math.floor((Math.random() * 8)));
arrayToBeFilled[i] = [randomNumber, randomNumber];
if (i < numbers.length - 1) {
assign(numbers, ++i);
}
}
assign(numbers, 0)
console.log(arrayToBeFilled);

Categories

Resources