I have a logic problem. I need to distribute an array of values evenly to another array of values. To illustrate:
const colors = ['green', 'yellow', 'red']
const plots = [0, 10, 40, 90, 150, 230, 250]
const withColors = plots.map(e => ({
value: e, color: ???
}))
/* expected output:
[
{value: 0, color: 'green'},
{value: 10, color: 'green'},
{value: 40, color: 'yellow'},
{value: 90, color: 'yellow'},
{value: 150, color: 'red'},
{value: 230, color: 'red'},
{value: 250, color: 'red'},
]
*/
current solution, I definitely have no idea yet, and I will update my question as I am currently brainstorming how to solve this.
Here's a brute force method where I iterate through the array and give each third of the plots a color.
Another way would be to write a helper function that handles turning the values of i/plots.length into 0, 1, or 2 and return that.
const colors = ['green', 'yellow', 'red'];
const plots = [0, 10, 40, 90, 150, 230, 250];
const result = {};
for (var i=0; i<plots.length; i++) {
var thisPlot = plots[i];
if (i / plots.length < 1/3.0) {
result[thisPlot] = colors[0];
}
else if (i / plots.length < 2/3.0) {
result[thisPlot] = colors[1];
}
else {
result[thisPlot] = colors[2];
}
}
console.log(result);
Can't see any logic behind this how this pattern will grow in future, but certainly you can try something like below.
// const colors = ['green', 'yellow', 'red']
const plots = [0, 10, 40, 90, 150, 230, 250]
function getColors(val) {
if(val >= 150 && val <=250) return "red";
if(val >= 40 && val <= 90) return "yellow";
return "green"
}
const withColors = plots.map(e => ({
value: e, color: getColors(e)
}))
Assigning colors one by one to each index
This will spread colors evenly, and is the simplest algorithm, but does not keep the colors grouped as in your example.
const colors = ['green', 'yellow', 'red']
const plots = [0, 10, 40, 90, 150, 230, 250, 260]
const withColors = plots.map((plot, i) => ({ value: plot, color: colors[i % colors.length]}));
console.log(withColors);
Keeping colors grouped
Just calculate the number of plots to map to each color, and assign each color to the appropriate number of plots.
One caveat: We require plots per color to be at least one in the case that there are less plots than colors. In this case, some colors will not be included.
There is one thing you haven't specified: what to do with the remainder. In your example plots.length / colors.length == 7 / 3 is 2 with a remainder of 1. In other words, 2 plots per color, and 1 plot left over. Having a remainder of 1 is easy: just assign any color to an extra plot. But what about larger remainders? There's a few strategies.
Assigning the last color to all trailing plots
Just round down plots per color and keep using the last color for any extra plots.
const colors = ['green', 'yellow', 'red']
const plots = [0, 10, 40, 90, 150, 230, 250, 260]
const plotsPerColor = Math.max(1, Math.floor(plots.length / colors.length));
let colorIdx = 0;
let count = 0;
const withColors = plots.map((plot) => {
const result = { value: plot, color: colors[colorIdx]};
if (++count === plotsPerColor && colorIdx < colors.length-1) {
colorIdx++;
count = 0;
}
return result;
})
console.log(withColors);
Spreading remainder evenly at end
Round down plots per color and increase it by one at the appropriate time.
const colors = ['green', 'yellow', 'red']
const plots = [0, 10, 40, 90, 150, 230, 250, 260]
let plotsPerColor = Math.max(1, Math.floor(plots.length / colors.length));
let remainder = plots.length % colors.length;
let colorIdx = 0;
let count = 0;
const withColors = plots.map((plot) => {
const result = { value: plot, color: colors[colorIdx]};
if (++count === plotsPerColor) {
if (++colorIdx === colors.length - remainder) plotsPerColor++;
count = 0;
}
return result;
});
console.log(withColors);
Spreading remainder evenly at beginning
Round up plots per color and decrease it by one at the appropriate time.
const colors = ['green', 'yellow', 'red']
const plots = [0, 10, 40, 90, 150, 230, 250, 260]
let plotsPerColor = Math.ceil(plots.length / colors.length);
let remainder = plots.length % colors.length;
let colorIdx = 0;
let count = 0;
const withColors = plots.map((plot) => {
const result = { value: plot, color: colors[colorIdx]};
if (++count === plotsPerColor) {
if (++colorIdx === remainder) plotsPerColor--;
count = 0;
}
return result;
});
console.log(withColors);
Related
I have weather temperature for the next few days. I want to map all values no matter how much to a scale of {0,1,2,3,4}
For this I am trying:
let scale = Math.round((todayTemp / maxTemp) * 5) - 1
With
markersIcons[0] = 'blue'
markersIcons[1] = 'purple'
markersIcons[2] = 'green'
markersIcons[3] = 'yellow'
markersIcons[4] = 'red'
and
markersIcons[scale]
I have undefined results. So much unpredictable results: {-1,5,...?}
I am not sure why is this, mathematically I am sure of the banal formulae.
You need to hand over a minimum value (inclusive) and a maximum value (exclusive) and the actual to get a color.
const
COLORS = ['blue', 'purple', 'green', 'yellow', 'red'],
getColor = (min, max, value) => COLORS[Math.floor(COLORS.length * (value - min) / (max - min))];
console.log(getColor(-10, 40, -10));
console.log(getColor(-10, 40, 0));
console.log(getColor(-10, 40, 10));
console.log(getColor(-10, 40, 20));
console.log(getColor(-10, 40, 30));
console.log(getColor(-10, 40, 39));
Having a hard time to group this array. any suggestions.
As an example I have an array var a = [10, 100, 20, 50, 20, 50, 70, 120]
and I have a maximum of 150 and minimum length of 3 i.e each sub array can have a total maximum sum of 150 and a maximum length of 3
any suggestion to make it like this [[10, 100, 20], [50, 20, 50], [70], [120]]
thanks in advance
Here you go, the groupArray function will iterate on your input and build groups based on max length and max sum provided.
function groupArray(input, maxSum, maxLen) {
const res = [[]];
let mark = 0;
let sum = 0;
input.forEach( ele => {
// if the current group has already reach maxLenght or maxSum
// then create a new group
if ( res[mark].length > (maxLen-1)
|| (sum + ele) > maxSum ) {
res.push([ele]);
mark++;
sum = ele;
}
// otherwise add to current grop
else {
res[mark].push(ele);
sum += ele;
}
});
return res;
}
const test_1 = [10, 100, 20, 50, 20, 50, 70, 120];
const test_2 = [10, 130, 20, 50, 20, 50, 70, 120];
const test_3 = [140, 110, 20, 50, 20, 50, 70, 120];
console.log(groupArray(test_1, 150, 3));
console.log(groupArray(test_2, 150, 3));
console.log(groupArray(test_3, 150, 3));
Note: Since the question did not have any additional rules, this function does not reorder the array or try to look for the best possible length match or best possible sum matches.
I have below array
const floorPerDayMilestones = [25, 50, 75, 100, 125, 150, 175, 200]
From the frontEnd user enters any number say it is
const number = 136
I need to find closest to the number but the lesser one.
So the output should be 125
Even if the number is 149 the output should be 125
How can I do this. I tried many way but could get the answer.
Thanks!!!
You can use Array.reduce for this
const floorPerDayMilestones = [25, 50, 75, 100, 125, 150, 175, 200]
function getClosestNumber(d) {
return floorPerDayMilestones.reduce((a, b) => b <=d && a < b ? b : a, 0 )
}
console.log(getClosestNumber(135) || 'No lesser number available')
console.log(getClosestNumber(149) || 'No lesser number available')
console.log(getClosestNumber(22) || 'No lesser number available')
Maybe you should look at this: get closest number out of array
And in your foreach, save your closest number in a var. Then check if your number is bigger than the one in your array . if yes, take your last var, else continue your foreach
You can sort the array in ascending order and then find the index of number that is immediately higher than number then one position less of that value will be the immediate number that is less than number
const floorPerDayMilestones = [25, 50, 75, 100, 125, 150, 175, 200]
const number = 136;
floorPerDayMilestones.sort((a,b)=> a-b);
var index = floorPerDayMilestones.findIndex(val => val>number);
var num = floorPerDayMilestones[index-1];
console.log(num);
Try this
const floorPerDayMilestones = [25, 50, 75, 100, 125, 150, 175, 200];
const number = 136;
const nextLesser = floorPerDayMilestones.reduce((nl, curr) => (curr <= number) && (curr > nl) ? curr : nl , 0)
console.log(nextLesser)
Used Array.prototype.reduce
You can also filter out numbers greater than given and select max from this subset.
const floorPerDayMilestones = [25, 50, 75, 100, 125, 150, 175, 200]
const number = 136
const filtered = floorPerDayMilestones.filter(el=>el<=number);
console.log(Math.max(...filtered))
Or, if you're already using lodash (only in this case - don't import lodash just to use solution below), you can do it with maxBy. maxBy treats numbers greater than number as null.
const floorPerDayMilestones = [25, 50, 75, 100, 125, 150, 175, 200]
const number = 136
let result = _.maxBy(floorPerDayMilestones, el=>el<=number?el:null);
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
With lodash you could use _.sortedIndex:
const numbers = [25, 50, 75, 100, 125, 150, 175, 200]
const closestNum = (arr, n) => {
let index = _.sortedIndex(arr, n)
return arr[index] == n ? arr[index] : arr[index-1]
}
console.log(closestNum(numbers, 135)) // 120
console.log(closestNum(numbers, 160)) // 150
console.log(closestNum(numbers, 180)) // 175
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
With JS you can simply use Array.reduceRight:
const numbers = [25, 50, 75, 100, 125, 150, 175, 200]
const closestNum = (arr, n) => arr.reduceRight((r,c) => !r && c < n ? c : r, 0)
console.log(closestNum(numbers, 135)) // 120
console.log(closestNum(numbers, 160)) // 150
console.log(closestNum(numbers, 180)) // 175
Since it will start from the right side all you care about is to find the first number less than your argument n.
You could also do Array.reverse and then just Array.filter (use Array.from if you do not want to mutate the array):
const numbers = [25, 50, 75, 100, 125, 150, 175, 200]
const closestNum = (arr, n) => arr.reverse().find(x => x < n)
// To not mutate use `Array.from`
// const closestNum = (arr, n) => Array.from(arr).reverse().find(x => x < n)
console.log(closestNum(numbers, 135)) // 120
console.log(closestNum(numbers, 160)) // 150
console.log(closestNum(numbers, 180)) // 175
If it's sorted in ascending order like in the question this should work.
const floorPerDayMilestones = [25, 50, 75, 100, 125, 150, 175, 200];
const number = 136;
function findClosest(arr, num) {
for (let i = 0; i < arr.length; ++i) {
if (arr[i] > num) {
return arr[i - 1];
}
}
}
console.log(findClosest(floorPerDayMilestones,number));
I do have an array of objects (book) here. My question is: How do I use the "color" object so each book has its color property? I think it has to be something like this -> book[amount].color <- but I can't make it work.
I don't know how to call the color property.
This is the array of objects:
var book = [
{title: "The Giver",
stars: 3,
author: "Lowry Loys",
color: color(0, 38, 255), //How do I make this property work in the code?
image: true},
{title: "How to win friends",
stars: 5,
author: "Dale Carnegie",
color: color(214, 255, 219),
image: false},
{title: "Principios fund. de la filosofía",
stars: 5,
author: "Georges Politzer",
color: color(115, 0, 255),
image: false}
];
This is the code
// draw shelf
fill(173, 117, 33);
rect(0, 120, width, 10);
// draw books + title + author
for (var amount = 0; amount < book.length; amount++) { //draw books
fill(214, 255, 219);
rect(154*amount, 20, 90, 100);
fill(0, 0, 0);
textSize(13);
text(book[amount].title, 5 + 158*amount, 27, 68, 100); //draw title
textSize(10);
text(book[amount].author, 5 + 155*amount, 91, 75, 100); //draw author
for (var s = 0; s < book[amount].stars; s++) { //draw stars
image(getImage("cute/Star"), 11 + s * 15 + amount * 151, 98, 15, 22);
}
for (var i = 0; i < book[amount].image; i++) { //draw stars
image(getImage("avatars/aqualine-sapling"), 9 + i * 60 + amount * 46, 42, 36, 39);
}
}
Assuming you don't have a color function, you'll have to quote those properties and then extract the color information.
In this simple example a regex grabs the color values from the (index 1) object, and then sets the background color of a temporary element.
var book=[{title:"The Giver",stars:3,author:"Lowry Loys",color:'color(0, 38, 255)',image:!0},{title:"How to win friends",stars:5,author:"Dale Carnegie",color:'color(214, 255, 219)',image:!1},{title:"Principios fund. de la filosofía",stars:5,author:"Georges Politzer",color:'color(115, 0, 255)',image:!1}]
const result = document.getElementById('result');
const color = book[1].color.match(/(\d+)/g);
result.style.backgroundColor = getColor(color);
function getColor(color) {
return `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
}
<div id="result">sdfdsfdsf</div>
The color function doesn't exist in plain javascript. I think the easiest way will be to store your colors as a HEXs in strings like below:
var book = [{
title: "The Giver",
stars: 3,
author: "Lowry Loys",
color: "#0026ff", // converted rgb(0, 38, 255)
image: true
}];
book[0].color; // "#0026ff"
You can use e.g. this tool to manually convert colors to HEX. If you don't like it just implement your own color function to convert rgb to hex (link).
Here you can also find an interesting npm package named Qix-/color:
JavaScript library for immutable color conversion and manipulation with support for CSS color strings.
Hope it helps!
Here is what i have got so far with help of stackoverflow answers.
I am able to add description to bars on the bar chart but now struggling with putting label and scale on Y axis.
window.onload = function ()
{
var r = Raphael("diagram"),
data3 = [25, 20, 13, 32, 15, 5, 6, 10],
txtattr = { font: "24px 'Allerta Stencil', sans-serif", fill: "rgb(105, 136, 39)" };
r.text(250, 10, "Sample Chart").attr(txtattr);
var bc = r.barchart(10, 10, 500, 400, data3,
{
stacked: false,
type: "sharp"
});
bc.attr({ fill: "#2f69bf" });
var x = 1;
labelBarChart(r, bc,
['abc', 'b', 'card', 'd', 'elph', 'fun', 'gurr', 'ha'],
{ fill: "#2f69bf", font: "16px sans-serif" }
);
};
function labelBarChart(r, bc, labels, attrs)
{
for (var i = 0; i < bc.bars.length; i++)
{
var bar = bc.bars[i];
var gutterY = bar.w * 0.4;
var labelX = bar.x;
var labelY = bar.y + bar.h + gutterY;
var labelText = labels[i];
var labelAttr = { fill: "#2f69bf", font: "16px sans-serif" };
r.text(labelX, labelY, labelText).attr(labelAttr);
}
}
I would like the graph to look like this with description and units on Y Axis, don’t need anything fancy like emboss effects etc.
!