I guess it is more a logical problem, but it also concerns reduce method (I guess it is a proper method to that kind of issue).
Here are my 3 arrays:
const availableHours = [ 9, 10, 11, 12, 13, 14 ];
const exArr1 = [ 9, 10, 11, 12, 13, 14 ]
const exArr2 = [ 10, 11, 12, 13 ]
The first one represents all available hours. The user always books two of them, being next to each other (f.ex. 9and10, 13and14, 10and11, and so on...) in any configuration. Now, if user books all three sets (i.e. 9-10, 11-12, 13-14) I need to return true. It means the day is full booked. But also if it is booked f.ex. like that 10-11 and 12-13 it also should return true, as those hours which have been left unbooked (9 and 14) cannot be booked as they are single hours. Both example arrays should return true.
Can you help on that one? I tried to do that with reduce method but could not do that.
Thank you!
This answer doesn't use Array.prototype.reduce, but I think it solves your problem.
It works by first removing any booked hours from the list of available hours. This assumes that the days bookings are correct (consecutively booked hourly pairs).
It then iterates through the remaining available hours and checks for consecutive hourly pairs. If none are found, the day is considered fully booked. If one or more is found, it isn't fully booked.
const available = [ 9, 10, 11, 12, 13, 14 ];
const day1 = [ 9, 10, 11, 12, 13, 14 ];
const day2 = [ 10, 11, 12, 13 ];
const day3 = [ 9, 10, 12, 13 ];
const day4 = [ 9, 10 ];
function isFullyBooked(day, available) {
const remaining = available.filter((hour) => ! day.includes(hour));
for (let i = 0; i < remaining.length; i++) {
if (remaining[i] + 1 === remaining[i + 1]) return false;
}
return true;
}
console.log(isFullyBooked(day1, available));
console.log(isFullyBooked(day2, available));
console.log(isFullyBooked(day3, available));
console.log(isFullyBooked(day4, available));
you can use includes and filter the array that contains the available hours and return the difference :
const availableHours = [9, 10, 11, 12, 13, 14];
const exArr1 = [9, 10, 11, 12, 13, 14]
const exArr2 = [10, 11, 12, 13]
const check = (arr, avail) => {
const diff = avail.filter(e => !arr.includes(e))
if (diff.length === 0)
console.log('all hours are booked')
else
console.log('unbooked hours : ', diff)
return true
}
console.log(check(exArr1, availableHours))
console.log(check(exArr2, availableHours))
You can use filter to find the blocks you need with something like:
const availableHours = [ 9, 10, 11, 12, 13, 14 ];
const exArr1 = [ 9, 10, 11, 12, 13, 14 ]
const exArr2 = [ 11, 12 ]
function getStartTimes(arr, taken) {
return arr.filter((item, index) => index != availableHours.length -1
&& !taken.includes(item)
&& !taken.includes(availableHours[index +1])
)
}
console.log(getStartTimes(availableHours, exArr1))
console.log(getStartTimes(availableHours, exArr2))
The returned array will be hours which can start 2-hour blocks. You can test the length of this array if you just want true or false.
This also has the advantage that it will still work if one hour blocks are reserved like [9, 12, 13, 14] (result should show a two hour block starting at 10 available)
In case you want to use Array.prototype.reduce
Here is a solution defined in a more functional manner.
const fullyBooked = (available, booked) =>
!booked.reduce(
// reducer zeros out booked hours from inverted array
(accumulator, currentValue) => (accumulator[currentValue] = 0, accumulator),
// initial value is available times inverted into an array of 1's and 0's 1 if available
available.reduce(
(accumulator2, currentValue2) => (accumulator2[currentValue2] = 1, accumulator2),
new Array(25).fill(0)
)
).some(
// not fully booked if two hours are sequentially available
(value, index, array) => value && array[index+1]
);
console.log(fullyBooked([9, 10, 11, 12, 13, 14], [9, 10, 11, 12, 13, 14]));
console.log(fullyBooked([9, 10, 11, 12, 13, 14], [9, 10, 11, 12, 13]));
console.log(fullyBooked([9, 10, 11, 12, 13, 14], [10, 11, 12, 13, 14]));
console.log(fullyBooked([9, 10, 11, 12, 13, 14], [9, 10, 13, 14]));
console.log(fullyBooked([9, 10, 11, 12, 13, 14], [9, 11]));
This doesn't use reduce either, but it does filter things properly, and I particularly like separating functions out for more readable/segmented code, rather than pushing all logic into one reducer.
const allAvailable = {
9: true,
10: true,
11: true,
12: true,
13: true,
14: true,
};
const exArr1 = [ 9, 10, 11, 12, 13, 14 ];
const exArr2 = [ 10, 11, 12, 13 ];
const exArr3 = [ 10, 11 ];
function remainingHours(hours) {
const stillAvailable = Object.assign({}, allAvailable);
hours.forEach(hour => {
delete stillAvailable[hour];
});
return Object.keys(stillAvailable);
}
function hasConsecutive(hoursAvailable) {
if (hoursAvailable.length < 2) {
return false;
}
return hoursAvailable.some((hour, index) => {
if (index < hoursAvailable.length - 1) {
return +hour + 1 === +hoursAvailable[index + 1];
}
});
}
function isBooked(hours) {
return !hasConsecutive(remainingHours(hours));
}
console.log(isBooked(exArr1));
console.log(isBooked(exArr2));
console.log(isBooked(exArr3));
Related
I have this function that assigns dynamic key to initialized object
function meanCluster(clusters) {
let initialized = {};
clusters.forEach(mean => {
initialized[mean] = [];
})
return initialized;
}
and I have these data
const OneDimensionPoint = [22, 9, 12, 15, 10, 27, 35, 18, 36, 11]
now I want to assign element 22, 9, and 12 as the key to my initialized variable inside my meanCluster function and to do that my approach is this code below.
let mean = [OneDimensionPoint[0], OneDimensionPoint[1], OneDimensionPoint[2]]
meanCluster(mean)
when I console.log(meanCluster(mean)) the result is this
{9: Array(0), 12: Array(0), 22: Array(0)}
I get 9, 12, 22 as the key instead of 22, 9, 12
my object is sorted, and I don't want. I also want to keep it as Number and not string.
can someone tell me why?
You can use a ES6 MAP:
const OneDimensionPoint = [22, 9, 12, 15, 10, 27, 35, 18, 36, 11] ;
let mean = [OneDimensionPoint[0], OneDimensionPoint[1], OneDimensionPoint[2]];
console.log(
mean.reduce((r,i)=>{
r.set(i,[])
return r;
}, new Map())
)
So I have two sets of Array, one of them is an object (arrA). And another one is just a set of primitive values arrB.
let a = "100229265852737908723455202093346882084130103685642861644052656467061936958706";
let arrA = [];
let arrB = [];
for (let i = 1; i <= 28; i++) {
arrA.push({index: i, pos: i, unique: false});
arrB.push(i);
}
let b = a.split(/(?=(?:..)*$)/).slice(0, 28);
b.forEach((val, index) => {
let c = Math.floor((+val / 100) * 28 ) + 1;
if (arrB.indexOf(c) !== -1) {
arrB.splice(arrB.indexOf(c), 1);
arrA[index].unique = true;
}
arrA[index].pos = c;
});
arrB.forEach((val, index) => {
arrA.forEach((valA, indexA) => {
if (!valA.unique) {
if (arrB[index] > valA.pos) {
arrA[indexA].pos = arrB[index];
arrA[indexA].unique = true;
arrB.splice(arrB.indexOf(arrB[index]));
}
}
})
});
My expected result is, arrA.pos is:
3, 1, 9, 8, 17, 15, 21, 4, 22, 10, 16, 6, 7, 27, 13, 20, 25, 5, 12, 14, 19, 11, 24, 18, 26, 28, 2
However I got:
3, 1, 9, 8, 17, 15, 21, 4, 21, 10, 16, 6, 6, 27, 10, 20, 23, 3, 12, 9, 3, 11, 24, 18, 8, 18, 18
If your goal is to take the string, split it into an array of elements of an arbitrary length, each being a 2-digit number, then output an Array that contains only the original Array's unique values, then you can do this:
const MAGIC_NUMBER = 28;
const a = "100229265852737908723455202093346882084130103685642861644052656467061936958706";
const b = a.split(/(?=(?:..)*$)/).slice(0, MAGIC_NUMBER );
const unique = Array.from( new Set( b ));
Set at MDN
If my Array1 is
Array1 = [1,2,3,4,5,6,7,8,9,10]
the Result should be the same as Combined_Array= [1,2,3,4,5,6,7,8,9,10]
if i got
Array2=[11,12,13,14,15,16,17,18,19,20]
the Resut should be Combined_Array =[1,2,3,4,5,11,12,13,14,15]
and if again i got
Array3=[21,22,23,24,25,26,27,28,19,30]
The Combined_array = [1,2,3,11,12,13,21,22,23,24]
and so on , Doesnt matter how much Array's i want that it should give me a Combined_Array from all the different Array with Fixed Length
Need a Function to make this work .
You could take a closure over the collected arrays and retuen an array of the parts which are difined by the count of arrays.
const
getCombined = (a) => {
const allArrays = [];
return (b => {
allArrays.push(b);
let i = 0,
p = Math.floor(10 / allArrays.length),
result = [];
while (i < allArrays.length) result.push(...allArrays[i++].slice(0, p));
while (result.length < 10) result.push(allArrays[allArrays.length - 1][p++]);
return result;
});
};
var c = [],
add = getCombined(c);
c = add([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
console.log(...c); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
c = add([11, 12, 13, 14, 15, 16, 17, 18, 19, 20]);
console.log(...c); // [1, 2, 3, 4, 5, 11, 12, 13, 14, 15]
c = add([21, 22, 23, 24, 25, 26, 27, 28, 29, 30]);
console.log(...c); // [1, 2, 3, 11, 12, 13, 21, 22, 23, 24]
You need to consider many corner cases (if result array length exceeds given arrays count, if given arrays length differs and so on).
This will work for the simple scenario:
const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const arr2 = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
const arr3 = [21, 22, 23, 24, 25, 26, 27, 28, 19, 30];
const combineArrays = (arr, length) => {
let elementsCount = Math.floor(length / arr.length);
const result = arr.reduce((acc, el) =>
acc.concat(el.slice(0, elementsCount)), []);
while (result.length < length)
result.push(...arr.pop().slice(elementsCount, ++elementsCount));
return result;
};
const result1 = combineArrays([arr1], 10); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const result2 = combineArrays([arr1, arr2], 10); // [1, 2, 3, 4, 5, 11, 12, 13, 14, 15]
const result3 = combineArrays([arr1, arr2, arr3], 10); // [1, 2, 3, 11, 12, 13, 21, 22, 23, 24]
If I have an array of items, such as,
const array = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 ]
How can I map it, so that the screen/page renders,
1 6 11 16
2 7 12 17
3 8 13 18
4 9 14
5 10 15
I was able to get it to kind of work horizontally with,
const chunkSize = 5;
array
.map((e, i) => {
return i % chunkSize === 0 ?
selected.slice(i, i + chunkSize)
: null;
})
.filter(e => e);
But I am unable to get it to work vertically. How can I do this?
Edit:
The suggested solution from another answer returns subarrays, which is not what I had asked in this question.
You could calculate the index for the row.
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18],
chunk = 5,
result = array.reduce((r, v, i) => {
(r[i % chunk] = r[i % chunk] || []).push(v);
return r;
}, []);
result.forEach(a => console.log(...a));
const array = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 ]
const chunkSize = 5;
let result = [];
for (let i = 0; i < chunkSize; i++) {
result[i] = [];
}
array.forEach((e,i) => {
result[i % chunkSize].push(e);
});
console.log(result);
/*
Result :
[ [ 1, 6, 11, 16 ],
[ 2, 7, 12, 17 ],
[ 3, 8, 13, 18 ],
[ 4, 9, 14 ],
[ 5, 10, 15 ] ]
*/
Here's a still-compact but readable version.
const columnize = (items, rows) => {
const toColumns = (table, item, iteration) => {
let row = iteration % rows;
table[row] = table[row] || [];
table[row].push(item);
return table;
};
return items.reduce(toColumns, []);
};
Which would be used as:
const numbers = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 ];
console.log(columnize(numbers, 5));
https://jsfiddle.net/69fshprq/
Here is a way to output it as the questions asks. I'm not paying strict attention to the spacing, I'll leave that to a string padding function or technique to implement.
const numbers = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 ]
// <pre id="out"></pre>
let out = document.getElementById('out')
let list = columnize(numbers, 5)
for (var column in list) {
var item = list[column]
var line = item.reduce((line, item) => line + item + ' ', '')
out.textContent += line + ' \n'
}
https://jsfiddle.net/t60rfcpe/
I have an array [1, 5, 20, 17, 6, 12, 13, 20, 1, 14, 20].
Whenever the element of the array is equal to 20 I would like to wrap it with an asterisk on either side like this [1, 5, *20*, 17, 6, 12, 13, *20*, 1 , 14, *20*].
How can I achieve this?
You can use map
let arr = [1, 5, 20, 17, 6, 12, 13, 20, 1, 14, 20]
let result = arr.map(o => o === 20 ? '*20*' : o);
console.log(result);
Doc: map()
You can use Arrays forEach to modify the elements of the array. elem is each element and i is the respective index. We are using forEach to modify the existing array. Since this is what you desired..
let arr = [1, 5, 20, 17, 6, 12, 13, 20, 1, 14, 20]
arr.forEach((elem, i) => {
if (elem === 20) {
arr[i] = "*20*"
}
})
console.log(arr)
function rollDice(max, times, bonus) {
var rolls = [1, 5, 20, 17, 6, 12, 13, 20, 1, 14, 20];
rolls.forEach((elem, i) => { if (elem === 20) { rolls[i] = "twenty" } });
for (var i = 0; times > i; i++)
{
max = Math.floor(max);
rolls.push(Math.floor(Math.random() * max) + 1 | + parseInt(bonus));
}
console.log(rolls);
}
rollDice(20, 5);
The problem you are experiencing is that you need to convert the integer number into strings. JavaScript has several ways to cleverly do this behind-the-scenes, but if you are still gaining an understanding of that, it's better to be explicit about what data types you start with (integers), and what data types you expect to end with (strings).
You transform the array, "mapping" over each item, transforming it to a string, and then if the string matches "20", you add the asterisks.
const start_array = [1, 5, 20, 17, 6, 12, 13, 20, 1, 14, 20];
const new_array = start_array.map((integer) => {
let number_string = integer.toString();
if (number_string === "20") {
number_string = "*" + number_string + "*";
}
return number_string;
})
console.log(new_array);