I have an array of items for which I want to create steps based on their index and the length of the array.
The minimum value should be 0 and the maximum 100.
I don't know if I'm explaining it right, but for example. I have this array:
const RAMmarks = [
{
value: 0,
label: ">1024 MB",
},
{
value: 25,
label: ">2048 MB",
},
{
value: 50,
label: ">4096 MB",
},
{
value: 75,
label: ">6144 MB",
},
{
value: 100,
label: ">8192 MB",
},
];
...but then the other one has less items:
const diskMarks = [
{
value: 0,
label: ">20 GB",
},
{
value: 33,
label: ">45 GB",
},
{
value: 66,
label: ">75 GB",
},
{
value: 99,
label: ">100 GB",
},
];
How do I get the values dynamically based on how many steps the array has?
You can do this easily with Array.from() and a map function. Due to how arrays are stored, {length: steps} acts as an array-like object with steps empty elements, then the map function transforms each empty element into its value. Try using this code:
function stepArray(min, max, steps) {
return Array.from({length: steps}, (_, i) => ((max-min)/(steps-1))*i + min);
}
console.log(stepArray(0, 100, 5));
The idea is to get the difference between max and min, divide that difference into step-1 steps (because we need to include both of the endpoints), multiply that step by the current step i, then add back the min that was subtracted in the beginning.
Note that if you are using uneven intervals, you might want to use Math.floor() to get nicer numbers. Expand the snippet below to see an example:
function stepArray(min, max, steps) {
return Array.from({length: steps}, (_, i) => Math.floor(((max-min)/(steps-1))*i + min));
}
// without Math.floor(), these would all be repeating decimals
console.log(stepArray(0, 100, 10));
Once you have this basic array of values, you can then easily transform it into your more complex array of marks.
I would do like this. Had to add the floor to make it work for your second example.
const RAMmarks = [
{
value: 0,
label: ">1024 MB",
},
{
value: 25,
label: ">2048 MB",
},
{
value: 50,
label: ">4096 MB",
},
{
value: 75,
label: ">6144 MB",
},
{
value: 100,
label: ">8192 MB",
},
];
const diskMarks = [
{
value: 0,
label: ">20 GB",
},
{
value: 33,
label: ">45 GB",
},
{
value: 66,
label: ">75 GB",
},
{
value: 99,
label: ">100 GB",
},
];
Array.prototype.addStep = function () {
return this.map((mark, index) => {
return {
label: mark.label,
value: index * Math.floor(100 / (this.length - 1)),
};
});
};
const RAMmarksStep = RAMmarks.addStep();
const diskMarksStep = diskMarks.addStep();
console.log(RAMmarksStep);
console.log(diskMarksStep);
You can use the following function to add the values to your items:
function addMarks(items) {
let steps = items.length - 1;
let step = 100 / steps;
for(let i in items) {
items[i].value = Math.round(i * step);
}
return items;
}
Based on the example you provided, you might want to swap Math.round for Math.floor
Related
I have a function inside of a react class component which generates properties. Since I want to be able to have duplicate properties, I've done it in a way that it is possible. However, I want those duplicate properties to be combined as a single value so that it can be displayed in the render function as a single property with a bigger value instead of 2 properties with smaller values. How can I achieve this based on the below code?
changePropertyState = () => {
let rngProperties = []
let maxProp = this.state.rarity.maxProperties;
let minProp = this.state.rarity.minProperties;
let rngCurrentPropAmount = Math.floor(Math.random() * (maxProp - minProp + 1) + minProp);
// the actual properties for an item based on the array of properties
for (let i = 0; i < rngCurrentPropAmount; i++) {
let rngNum = Math.floor(Math.random() * (itemProperties.length))
rngProperties.push(itemProperties[rngNum])
}
let proxyProperties = []
// setting the values for each property based on the min and max of that property
for (let j = 0; j < rngProperties.length; j++) {
let rngValue = this.getRandomNumber(rngProperties[j].min, rngProperties[j].max);
rngProperties[j].value = rngValue;
// creating a proxy to store unique values for each property,
let obj = {
id: rngProperties[j].id,
name: rngProperties[j].name,
min: rngProperties[j].min,
max: rngProperties[j].max,
value: rngProperties[j].value
}
proxyProperties.push(obj);
}
//setState() has an inbuilt functionality for a callback function, in which we can console.log the newly changed state
this.setState({
properties: proxyProperties
}, () => {
// console.log('propF', this.state)
});
}
An expected output of the above code is the below picture.
What I want to do is combine the 2 properties called (in this case) "Area Damage" so that only 1 property is listed but the value is 25 (again, in this case).
The itemProperties is an array of objects that have the following structure:
id: 1,
name: "intelligence",
min: 1,
max: 10,
value: 0
The rngCurrentPropAmount can be replaced with any integer for testing purposes. This is the amount of properties to be added.
The logic is to first group the array by name then merge them using reduce & summing the value. Bit tricky but working. Hope this is what was needed. The initial array has 4 elements & the final one has two. value is summed up.
const arr = [
{
id: 1, name: "intelligence", min: 1, max: 10, value: 11
},
{
id: 1, name: "intelligence", min: 1, max: 10, value: 4
},
{
id: 2, name: "dexterity", min: 1, max: 10, value: 3
},
{
id: 2, name: "dexterity", min: 1, max: 10, value: 8
}
];
//group an array by property
function groupBy(arr, property) {
return arr.reduce(function(memo, x) {
if (!memo[x[property]]) {
memo[x[property]] = [];
}
memo[x[property]].push(x);
return memo;
}, {});
}
//group by name
const grouped = groupBy(arr, "name");
const keys = Object.keys(grouped);
var output = [];
//loop keys
keys.forEach(key => {
//merge using reduce
const out = grouped[key].reduce((acc, current) => {
return {
id: current.id,
name: current.name,
min: current.min,
max: current.max,
value: acc.value + current.value
}
});
output.push(out);
});
console.log(output);
I need to understand the simplest way of doing this.
I've got an array of objects:
const data = [
{
group: 'A',
incomes: {
"2019-12": 100,
"2020-12": 200,
"2021-12": 15
}
},
{
group: 'B',
incomes: {
"2019-12": 25,
"2020-12": 50,
}
}
]
What I'm trying to get is simple object where its key is the month from data.incomes and the value is sum of relative month values, so the final result looks like:
const totalIncomes = {
"2019-12": 125,
"2020-12": 250,
"2021-12": 15
}
Can anybody explain it to me step by step, please?
solved using reduce and forEach
Inside the reduce function I'm running a forEach on the array of keys of the incomes object/attribute. For each key which is a date I'm checking if the accumulator of the reduce function contains an attribute for each date and creates if not. After creating the attribute I'm summing the value for the current date attribute.
const data = [{
group: 'A',
incomes: {
"2019-12": 100,
"2020-12": 200,
"2021-12": 15
}
},
{
group: 'B',
incomes: {
"2019-12": 25,
"2020-12": 50,
}
}
]
const totalIncomes = data.reduce((acc, curr) => {
Object.keys(curr.incomes).forEach((key, index) => {
if (!acc[key]) {
acc[key] = 0
}
acc[key] += curr.incomes[key]
})
return acc
}, {})
console.log(totalIncomes)
Maybe this is not the pretties solutions but you can do it like this, the function is of course not necessary.
const data = [
{
group: "A",
incomes: {
"2019-12": 100,
"2020-12": 200,
"2021-12": 15,
},
},
{
group: "B",
incomes: {
"2019-12": 25,
"2020-12": 50,
},
},
];
getterInformation(data);
function getterInformation(object) {
let objectWithCalculatedValues = {};
object.forEach((items) => {
for (const key in items.incomes) {
if (objectWithCalculatedValues[key] === undefined) {
objectWithCalculatedValues[key] = 0;
}
objectWithCalculatedValues[key] += items.incomes[key];
}
});
console.log(objectWithCalculatedValues);
}
Assuming that this information may be useful to readers who may be unable to obtain necessary guidance (due to various possible reasons), here is one possible way to achieve the objective (solution):
const aggregateIncomesByMonth = () => (
data.map(d => Object.entries(d.incomes).map(([k, v]) => ({
key: k,
value: v
}))).flat().reduce((fin, itm) => ({
...fin,
[itm.key]: (fin[itm.key] || 0) + itm.value
}), {})
);
Explanation
Extract only the incomes from the data array
For each income object, get the key-value pair and transform into another object of the structure {key: 20yy-mm, value: nn}
Use .flat() to transform the result from step-2 into a 1-dimensional array
Use .reduce to sum the value for those cases where the key (ie, 20yy-mm) matches.
Code-snippet
const data = [{
group: 'A',
incomes: {
"2019-12": 100,
"2020-12": 200,
"2021-12": 15
}
},
{
group: 'B',
incomes: {
"2019-12": 25,
"2020-12": 50,
}
}
];
const aggregateIncomesByMonth = () => (
data.map(d => Object.entries(d.incomes).map(([k, v]) => ({
key: k,
value: v
}))).flat().reduce((fin, itm) => ({
...fin,
[itm.key]: (fin[itm.key] || 0) + itm.value
}), {})
);
console.log(aggregateIncomesByMonth());
My approach here is to destructure the array. This way I have all the data of the incomes of group A in the variable A and the same for B.
Then I do a double loop to compare both objects data and see if the dates match. If so, sum the incomes and add the data to the total object.
const data = [
{
group: 'A',
incomes: { "2019-12": 100, "2020-12": 200, "2021-12": 15 }
},
{
group: 'B',
incomes: { "2019-12": 25, "2020-12": 50 }
}
]
let A, B, total = {};
[A, B] = [data[0].incomes, data[1].incomes]
for(const date in A){
for(const d in B){
total[date] = date === d ? A[date] + B[date] : A[date]
}
}
console.log(total)
I'm on my way to create my first KNN Algo to learn machine learning.
I'm looking after a basic course online that's explaining it, I'm feeling that I did exactly the same as he did.
But when I'm running it I get this pretty basic error of js.
I am using TensorFlow.
.sort((a, b) => (a.get(0) > b.get(0) ? 1 : -1))
^
TypeError: a.get is not a function
require('#tensorflow/tfjs-node');
const tf = require('#tensorflow/tfjs');
const loadCSV = require('./load-csv');
function knn(features, labels, predictionPoint, k) {
return (
features
.sub(predictionPoint)
.pow(2)
.sum(1)
.pow(0.5)
.expandDims(1)
.concat(labels, 1)
.unstack()
.sort((a, b) => (a.get(0) > b.get(0) ? 1 : -1))
.slice(0, k)
.reduce((acc, pair) => acc + pair.get(1), 0) / k
);
}
let { features, labels, testFeatures, testLabels } = loadCSV(
'kc_house_data.csv',
{
shuffle: true,
splitTest: 10,
dataColumns: ['lat', 'long'],
labelColumns: ['price'],
}
);
features = tf.tensor(features);
labels = tf.tensor(labels);
console.log(features, labels, tf.tensor(testFeatures[0]), 10);
const result = knn(features, labels, tf.tensor(testFeatures[0]), 10);
console.log('Guess', result, testLabels[0][0]);
console.log(features);
the log on the top to see whats passing in the function.
Tensor {
kept: false,
isDisposedInternal: false,
shape: [ 21602, 2 ],
dtype: 'float32',
size: 43204,
strides: [ 2 ],
dataId: { id: 0 },
id: 0,
rankType: '2'
} Tensor {
kept: false,
isDisposedInternal: false,
shape: [ 21602, 1 ],
dtype: 'float32',
size: 21602,
strides: [ 1 ],
dataId: { id: 1 },
id: 1,
rankType: '2'
} Tensor {
kept: false,
isDisposedInternal: false,
shape: [ 2 ],
dtype: 'float32',
size: 2,
strides: [],
dataId: { id: 2 },
id: 2,
rankType: '1'
} 10
After long research, and a lot of time.
TensorFlow removed the .get function you would use instead arraySync.
for example.
pair.get(1)[0]
will be:
pair.arraySync(1)[0]
In the below object, I want to increment the value of data[1]'s val by 1, and leave everything unchanged, how can I achieve it?
const state =
{
"data": [{
"val": 1,
"other": 10
},
{
"val": 11,
"other": 100
},
{
"val": 100,
"other": 1000
}
]
}
I want the mutated object to be like this-
{
"data": [{
"val": 1,
"other": 10
},
{
"val": 10,
"other": 100
},
{
"val": 100,
"other": 1000
}
]
}
I know that I can change the value directly like this- state.data[1].val = state.data[1].val+1, but I want to achieve the same using spread operator, is it possible to achieve it using spread operator?
Somthing like this-
const mutatedState = {
...state,
data: [...state.data]
}
Get the data out of the object. And use like this
const state = { "data": [{ "val": 1, "other": 10 }, { "val": 11, "other": 100 }, { "val": 100, "other": 1000 } ] }
const {data} = state;
let res = {
...state,
data:[
data[0],
{...data[1],val:data[1].val+ 1},
...data.slice(2)
]
}
console.log(result)
You could assign parts of the array/objects.
var object = { data: [{ val: 1, other: 10 }, { val: 10, other: 100 }, { val: 100, other: 1000 }] },
result = {
...object,
data: Object.assign(
[...object.data],
{
1: Object.assign(
{},
object.data[1],
{ val: object.data[1].val + 1 }
)
}
)
};
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
With a bit help of some functional helpers that is actually quite elegant:
const mapOne = (index, mapper) => array => array.map((it, i) => i === index ? mapper(it) : it);
const lens = (key, mapper) => obj => ({ ...obj, [key]: mapper(obj[key]) });
// somewhere
this.setState(mapOne(1, lens("val", it => it + 1)));
How can i multiply the length of the nested values array using this array:
const options = [
{
name: "Colors",
values: [{
label: "Blue"
},
{
label: "Red"
}]
},
{
name: "Sizes",
values: [{
label: "Small"
},
{
label: "Large"
}]
}
]
My code:
const options = [
{
name: "Colors",
values: [
{
label: "Blue"
},
{
label: "Red"
}
]
},
{
name: "Sizes",
values: [
{
label: "Small"
},
{
label: "Large"
}
]
}
];
const multiply = options.reduce((a, b) => a * b.values.length, 0);
console.log(multiply);
I've tried with reduce but always return 0. It should return ex. 4
playground: https://codesandbox.io/s/08jrnr72w
edit: thanks everyone for the answers, I missed initial position...
The reduce function expects an initial value. The initial value you provided is 0. If you multiply 0 with anything, you'll get 0.
You should initialize your accumulator with 1. That should fix it.
But you might have to do more, i.e. handle the edge condition. For example, at one point you may get 0 as length for one particular object and in that case, your accumulator will evaluate to 0.
You need to initialize Array.prototype.reduce with 1 for multiplication (multiplying by zero is always going to end up as zero):
const multiply = options.reduce((a, b) => a * b.values.length, 1);
Map them to lengths and then reduce the results.
const options = [
{
name: "Colors",
values: [{
label: "Blue"
},
{
label: "Red"
}]
},
{
name: "Sizes",
values: [{
label: "Small"
},
{
label: "Large"
}]
}
]
const multValues = options.map(opt => opt.values.length).reduce((a,b) => a*b);
console.log(multValues)
Change your starting value to 1 as 0 times anything is 0 (thus your accumulator (a) will always be 0):
const options=[{name:"Colors",values:[{label:"Blue"},{label:"Red"}]},{name:"Sizes",values:[{label:"Small"},{label:"Large"}]}];
// change starting value -------------------------------------\/
const multiply = options.reduce((a, b) => a * b.values.length, 1);
console.log(multiply);