I'm trying to loop through 2 of the key value pairs in my state. They both have very similar names the only difference between them is that one ends in Day and the other ends in Night. Here is a simplified example of my project right now. My objective is for the log to come out with the results shown below:
state = {
energyType: "Electricity",
meterRate: "oneRate",
firstEnergyAmountDay: 1,
firstEnergyAmountNight: 2,
}
let days
if (this.state.energyType === "Electricity" && this.state.meterRate === "twoRate") {
days = ["Day", "Night"]
} else {
days = ["Day"]
}
days.map(day => {
console.log(this.state.firstEnergyAmount + day);
})
Right now my log just returns:
undefinedDay
undefinedNight
And I want the log to give me:
1
2
To do a computed property of an object, you need to use square brackets and a string
console.log(this.state["firstEnergyAmount" + day])
So that will concatenate strings together to produce either "firstEnergyAmountDay" or "firstEnergyAmountNight", and then the square brackets will look up the property with that key.
Related
I have an array with nested objects that looks like the one below.
What I'd like to do is loop through it calculate the sum of each item per date.
For example pc + screen = ?
I cannot seem to figure out how to do it properly. I have found this solution, it works great in console.log() but I cannot figure out how to output the result in a div. Should I use a map function ?
const amountPerDate = data.forEach(function (i) {
const sum = i.item.reduce(function (sum, elem) {
return sum + elem.price;
}, 0);
console.log("the total sum is " + sum);
});
The array:
The code you have posted doesn't seem quite right since forEach won't return anything, and the inner variable sum is not actually available for React to render since it is not in scope (in JavaScript, variables can not escape their containing function, which is function (i) { -- nothing outside of that function can see it).
You were roughly on the right tracks with needing map since that will return an array that represents an accumulation of the return values in the nested callback.
const amountsPerDate = data.map((i) => {
return i.item.reduce(function (sum, elem) {
return sum + elem.price;
}, 0);
});
amountsPerDate will now be an array of the sums. However, in this process, youve lost the info about which sum correlates to which date. So we need more. We can modify to return both the sum alongside the date (an array of objects, each with a sum and date inside).
const amountsPerDate = data.map((i) => {
return {
sum: i.item.reduce(function (sum, elem) {
return sum + elem.price;
}, 0),
date: i.date
});
Now, you should have something in amountsPerDate that looks like this:
[
{ date: '01/01/2022', sum: 200 },
{ date: '02/01/2022', sum: 30},
]
To display in your react component, it's just a case of rendering it, which will require you to map over this new data and return an element for each entry. You haven't posted your full component, but it will be something like this in your JSX:
<div>
{amountsPerDate.map(sum =>
<div>Date: {sum.date}. Total: {sum.sum}</div>
)}
</div>
Of course you can play with this and move it around as you see fit so it fits however you want it laid out.
It's really worth your time understanding map and the differences with foreach since it's so ubiquitous in functional programming. Foreach and map both loop over each item. But map allows you to return a value within the loop callback, and that value goes on to be part of a new array returned from map that represents that item. You can think of it as a transformation from one array to another -- both with the same length -- but with each item replaced with something of your choosing, calculated from each items original contents.
i'm currently learning JavaScript and have to do this challenge for one of the coding challenges and the challenge is :
"Create an object called 'scorers' which contains the name of the players who scored as properties and the number of goals as the value. in this game it will look like this:
{
Gnarby: 1,
Hummels: 1,
Lewandowski: 2
}"
the solution :
"loop over the array, and add the array elements as object properties,
and then increase the count as we encounter a new occurrence of a certain element"
the object :
const game = {
scored: ['Lewandowski', 'Gnarby', 'Lewandowski', 'Hummels']
};
the solution is :
const scorers = {};
for (const player of game.scored) {
scorers[player] ? scorers[player]++ : (scorers[player] = 1);
}
and the outcome is :
[Lewandowski: 2, Gnarby: 1, Hummels: 1]
i don't understand exactly what happens at the scorers[player] ? scorers[player]++ : (scorers[player] = 1);
what the scorers[player]++ do ?
It's a ternary operator that checks whether game contains the value of player as a property. If so, it increments the value of that property. Otherwise, it sets it to 1.
It can also be rewritten like so:
if(scorers[player]){
scorers[player]++;
} else {
scorers[player] = 1;
}
If scorers does not have the value of player as a property, scorers[player] will return undefined, which, when coerced to a boolean, is false.
I've run into a bit of an issue trying to map a list of items in javascript. I have the following array that I'm trying to map:
[{"project_id":"EGNL1701","title":"Test Energy Project",
"reservations":
[{"start_time":"1519887600000"},{"start_time":"1519891200000"},
{"start_time":"1519938000000"},{"start_time":"1519898400000"},
{"start_time":"1519902000000"},{"start_time":"1519905600000"},
{"start_time":"1519909200000"},{"start_time":"1529683200000"},
{"start_time":"1529686800000"},{"start_time":"1531893600000"},
{"start_time":"1531897200000"},{"start_time":"1531900800000"},
{"start_time":"1531904400000"}]},
{"project_id":"LENL1701","title":"Vive","reservations":[]}]
Basically it's a list of reservations per project. All the times are in unix code in miliseconds. I have no issue trying to convert the times to the right time, however what I do need is to check for reservation blocks of consecutive hours. A reservation is always an hour, so I want to map reservations that are directly after the previous reservation, to be included into the previous reservation so that it forms 1 reservation of multiple hours. So if the list would contain 3 reservations for 10-07-2018 at 10, 11 and 12 o'clock then it should combine those in one object.
The new array should look like this:
[
{
title: 'Energy Project',
startTime: 1530631437,
endTime: 1530638640
},
{
title: 'HTC VIVE',
startTime: 1530794845,
endTime: 1530797390
}
];
I'm not sure what the best way is to go about this, I was trying to mess with while and for loops to get it done, but I keep getting stuck in an infinite loop.
Here's the code I have now for checking if the next reservation is an hour after the current reservation in the loop:
while (moment.duration(getUnixTime(reservations[index + nextIndex].start_time).diff(getUnixTime(reservation.start_time))).asHours() === 1) {
nextIndex++;
}
I was hoping one of you might have a good idea on how to do this? I feel like I'm going about this all wrong. It's for a React project, I can use ES6 functions etc.
This might not be complete answer so try this
Create an empty reservations object R
Loop through (for each or for ) upto second last item. You can check if next item exists or not.
Check if the criteria for example next one is one hour after the current one then add them together and add it to R.
End time of R will become end time of current reservation or the one next to it.
Move to next one or skip based on what you did in previous step.
When you reach last item simply add it to R if its not being added in previous step.
One of the assumption is that all reservations are in order. You can order them via sorting functions to avoid loop and check where it fits.
I've fixed the issue using #Farrukh Subhani's suggestion.
First had to sort the list, then loop through it twice. Resulting code:
export const mapReservationTimes = reservations => {
const upcomingReservations = [];
reservations.sort((x, y) => {
return getUnixTime(x.start_time).toDate() - getUnixTime(y.start_time).toDate();
});
for (let index = 0; index < reservations.length; index++) {
let newReservation = { startTime: '', endTime: '' };
let reservation = reservations[index];
newReservation.startTime = getUnixTime(reservation.start_time).format('DD-MM-YYYY HH:MM');
newReservation.endTime = getUnixTime(reservation.start_time)
.add(1, 'hours')
.format('DD-MM-YYYY HH:MM');
for (let i = index; i < reservations.length - 1; i++) {
reservation = reservations[index];
const nextReservation = reservations[i + 1];
if (
moment
.duration(
getUnixTime(nextReservation.start_time).diff(
getUnixTime(reservation.start_time)
)
)
.asHours() === 1
) {
index++;
newReservation.endTime = getUnixTime(nextReservation.start_time)
.add(1, 'hours')
.format('DD-MM-YYYY HH:MM');
} else {
break;
}
}
upcomingReservations.push(newReservation);
}
return upcomingReservations;
};
Thanks a lot for the suggestion, it got me on the right track :)
I feel this might be a good fit for an array.reduce, though this might stem from my personal taste. This solution can and should be simplified and cleaned, but I tried to convey the general idea so I made it a little more verbose then I think production code should be.
const input = [{"project_id":"EGNL1701","title":"Test Energy Project","reservations":
[{"start_time":"1519887600000"},{"start_time":"1519891200000"},
{"start_time":"1519938000000"},{"start_time":"1519898400000"},
{"start_time":"1519902000000"},{"start_time":"1519905600000"},
{"start_time":"1519909200000"},{"start_time":"1529683200000"},
{"start_time":"1529686800000"},{"start_time":"1531893600000"},
{"start_time":"1531897200000"},{"start_time":"1531900800000"},
{"start_time":"1531904400000"}]},
{"project_id":"LENL1701","title":"Vive","reservations":[]}]
const hourInMilliseconds = 3600000
console.log(input.reduce((acc, project) => {
const title = project.title
const sortedReservations = project.reservations.map(r => r.start_time).sort()
const reservationCount = sortedReservations.length
const earliest = reservationCount > 0 ? sortedReservations[0] : "none"
const latest = reservationCount > 0 ? sortedReservations[reservationCount - 1] : "none"
const projectObject = {
title,
startTime: earliest,
endTime: isNaN(latest) ? latest : (Number(latest) + hourInMilliseconds).toString()
}
acc.push(projectObject)
return acc
}, [])
)
Disclaimer: This assumes that all the reservations in one array item are consecutive, which seems to be the case in your sample data. If this is not the case the function would have to be altered a bit.
I've done some research on this issue. I am trying to manipulate an array of calculated values that looks like this in the console:
{nodeVoltages: Array(11), totalPower: Array(1), xlength: Array(11)}
nodeVoltages: Array(11)
0:48
1:47.71306060387108
2:47.250273223993105
3:46.59686907269243
4:45.71876416434013
5:44.53304242029258
6:42.745236969423615
7:Complex {re: 40.38334500994142, im:1.919295696316476, __ember1513267958317: "ember368"}
8:Complex { re:39.55961661806138, im:3.8933604519196416, __ember1513267958317: "ember369"}
This array is created dynamically through some math that I've come up with so there is no input data that I can give you. I'm trying to make the above array look like this:
{nodeVoltages: Array(11), totalPower: Array(1), xlength: Array(11)}
nodeVoltages: Array(11)
0:48
1:47.71306060387108
2:47.250273223993105
3:46.59686907269243
4:45.71876416434013
5:44.53304242029258
6:42.745236969423615
7:40.38334500994142
8:39.55961661806138
Using mathjs, I was able to evaluate my expressions and dynamically add the values into an array with the array.push command and display them. However, my code breaks once the imaginary values pop up in the results of my array.
How can I remove these imaginary numbers from my array? In other words, I need to remove the "im:" parts of the values when they begin to appear before I push them to the displayed array.
I tried to do this with some code I found from a previous answer to someone else's question (How do I remove a particular element from an array in JavaScript?) splice command like this:
var nodeVoltage2 = parser.eval(expression2);
//checks if there are imaginary values and removes them
if ("im" in nodeVoltage2) {
nodeVoltage2.splice(2,1)
}
//adds value to result array for analysis
nodeVoltages.push(nodeVoltage2);
but it returns in the console that "im is not defined".
Any help is greatly appreciated!
You can use the array map function.
Basically, we loop through the array. If the item has a .re property, we take that value only. If there is no .re property, we keep the value as is.
We can either write that in shorthand, as with result using the ternary operator and arrow function, or we can write it in a slightly more verbose but traditional way, as with resultTwo
let data = [
48
,47.71306060387108
,47.250273223993105
,46.59686907269243
,45.71876416434013
,44.53304242029258
,42.745236969423615
,{re: 40.38334500994142, im:1.919295696316476, __ember1513267958317: "ember368"}
,{ re:39.55961661806138, im:3.8933604519196416, __ember1513267958317: "ember369"}
]
let result = data.map((x) => x && x.re ? x.re : x);
let resultTwo = data.map(function(elem) {
// First, we need to check that the array element is not null / undefined
// We then need to check that it has a property called re that is also not null / undefined
if (elem != null && elem.re != null) {
// Just return the property we're interested in
return elem.re;
} else {
// Return the element as is
return elem;
}
});
console.log(result);
console.log(resultTwo);
Given an example input:
[
{"id":1,"currentBlack":1,"currentWhite":0,"max":1},
{"id":2,"currentBlack":0,"currentWhite":1,"max":1},
]
Output all possible states of the input where currentBlack and currentWhite can have a value anywhere in the range from their initial value up to the maximum value.
Correct output for this example:
[
[
{"id":1,"currentBlack":1,"currentWhite":0,"max":1},
{"id":2,"currentBlack":0,"currentWhite":1,"max":1},
],
[
{"id":1,"currentBlack":1,"currentWhite":1,"max":1},
{"id":2,"currentBlack":0,"currentWhite":1,"max":1},
],
[
{"id":1,"currentBlack":1,"currentWhite":1,"max":1},
{"id":2,"currentBlack":1,"currentWhite":1,"max":1},
],
[
{"id":1,"currentBlack":1,"currentWhite":0,"max":1},
{"id":2,"currentBlack":1,"currentWhite":1,"max":1},
]
]
The real input will have max anywhere between 1 and 8 and there will be far more objects within the input array. My attempt is below (heavily commented):
function allPossibleCounts(pieceCounts) {//pieceCounts is the input
var collection = []; //used to collect all possible values
recursiveCalls(pieceCounts); //runs recursive function
return collection; //returns result
function recursiveCalls(pieceCounts) {
//if pieceCounts is already in collection then return, not yet implemented so duplicates are currently possible
collection.push(pieceCounts);//inputs a potential value
console.log(JSON.stringify(pieceCounts));//this is successfully logs the correct values
console.log(JSON.stringify(collection));//collection isn't correct, all values at the top of the array are copies of each other
for (let n in pieceCounts) {//pieceCounts should be the same at the start of each loop within each scope, aka pieceCounts should be the same at the end of this loop as it is at the start
subBlackCall(pieceCounts);
function subBlackCall(pieceCounts) {
if (pieceCounts[n].currentBlack < pieceCounts[n].max) {
pieceCounts[n].currentBlack++;//increment
recursiveCalls(pieceCounts);
subBlackCall(pieceCounts);//essentially you're either adding +1 or +2 or +3 ect all the way up to max and calling recursiveCalls() off of each of those incremented values
pieceCounts[n].currentBlack--;//decrement to return pieceCounts to how it was at the start of this function
}
}
subWhiteCall(pieceCounts);
function subWhiteCall(pieceCounts) {
if (pieceCounts[n].currentWhite < pieceCounts[n].max) {
pieceCounts[n].currentWhite++;
recursiveCalls(pieceCounts);
subWhiteCall(pieceCounts);
pieceCounts[n].currentWhite--;
}
}
}
}
}
But currently my attempt outputs as this ungodly mess of copied arrays
[[{"id":1,"currentBlack":1,"currentWhite":1,"max":1},{"id":2,"currentBlack":1,"currentWhite":1,"max":1}],[{"id":1,"currentBlack":1,"currentWhite":1,"max":1},{"id":2,"currentBlack":1,"currentWhite":1,"max":1}],[{"id":1,"currentBlack":1,"currentWhite":1,"max":1},{"id":2,"currentBlack":1,"currentWhite":1,"max":1}],[{"id":1,"currentBlack":1,"currentWhite":1,"max":1},{"id":2,"currentBlack":1,"currentWhite":1,"max":1}],[{"id":1,"currentBlack":1,"currentWhite":1,"max":1},{"id":2,"currentBlack":1,"currentWhite":1,"max":1}]]
Edit: working code: https://pastebin.com/qqFTppsY
The pieceCounts[n] always reference to the one object. You should recreate the pieceCount for saving in to the collection as different object. For example, you can add
pieceCounts = JSON.parse(JSON.stringify(pieceCounts)); // just clone
at the start of recursiveCalls function.
To avoid conversion to JSON and back, I would suggest using Object.assign to perform a deeper copy in combination with map on the array:
function allPossibleCounts(pieceCounts) {
var result = [],
current = deeperCopy(pieceCounts);
function deeperCopy(arr) {
return arr.map( row => Object.assign({}, row) );
}
function recurse(depth) {
// depth: indication of which value will be incremented. Each "row" has
// 2 items (black/white), so when depth is even, it refers to black, when
// odd to white. Divide by two for getting the "row" in which the increment
// should happen.
var idx = depth >> 1, // divide by 2 for getting row index
prop = depth % 2 ? 'currentWhite' : 'currentBlack', // odd/even
row = pieceCounts[idx];
if (!row) { // at the end of the array
// Take a copy of this variation and add it to the results
result.push(deeperCopy(current));
return; // backtrack for other variations
}
for (var value = row[prop]; value <= row.max; value++) {
// Set the value of this property
current[idx][prop] = value;
// Collect all variations that can be made by varying any of
// the property values that follow after this one
recurse(depth+1);
// Repeat for all higher values this property can get.
}
}
recurse(0); // Start the process
return result;
}
// Sample input
var pieceCounts = [
{"id":1,"currentBlack":1,"currentWhite":0,"max":1},
{"id":2,"currentBlack":0,"currentWhite":1,"max":1},
];
// Get results
var result = allPossibleCounts(pieceCounts);
// Output
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The idea is to use recursion: imagine the problem can be solved for all variations that can be made for all properties, except the first one. Produce those, and then change the first property value to the next possible value. Repeat again the production of all variations, etc. The combination of all those results together will be the solution for when the first property value should also be varied.
This is an ideal situation for recursion. The recursion stops when there are no more property values remaining: in that case there is only one solution; the one with all the values set as they are. It can be added to the list of results.
The properties can be enumerated like this:
row currentBlack currentWhite
---------------------------------
0 0 1
1 2 3
2 4 5
3 6 7
...
n 2n-2 2n-1
We could call that number depth, and increase it at every step of deeper recursion. Given a depth, the property to vary is defined by:
depth is even => currentBlack
depth is odd => currentWhite
row number = depth / 2 (ignoring the remainder)