Return value of recursive function is 'undefined' - javascript

Whenever I execute this snippet the console.log before return returns the array with 20 times the value 23.
However console.log(Check(users, 0, 20)); returns only 'undefined'.
What am I doing wrong?
var users = [23, 23, 23, 23, 23, 23, 23, 23, 23, 23];
console.log(Check(users, 0, 20));
function Check(ids, counter, limit){
ids.push(23);
// Recursion
if (counter+1 < limit){
Check(ids, counter+1, limit);
}
else {
console.log(ids);
return ids;
}
}

You forgot to return a result from the point, where you entering recusrion.
var users = [23, 23, 23, 23, 23, 23, 23, 23, 23, 23];
console.log(Check(users, 0, 20));
function Check(ids, counter, limit){
ids.push(23);
// Recursion
if (counter+1 < limit){
return Check(ids, counter+1, limit); // return here!
}
else {
console.log(ids);
return ids;
}
}
But return value seems useless, cause' your function altering initial array as well.

Related

How to send the inner function an array as a parameter?

The second function will take the array as a parameter
How would you send the inner function as a parameter?
const score = [98, 76, 94, 82, 70, 95, 45, 90]
const determinePass = function (threshold) {
return function (array) {
return array.map(function (value) {
return value > threshold ? "pass" : "fail";
})
}
}
this is how you call the return function.
determinePass(threshold)(array);
// for example
// determinePass(50)(score)
const score = [98, 76, 94, 82, 70, 95, 45, 90]
const determinePass = function (threshold) {
return function (array) {
return array.map(function (value) {
return value > threshold ? "pass" : "fail";
})
}
}
console.log(determinePass(50)(score));
This returns an array of pass or fail. The parameter is in the determinePass function.
const score = [98, 76, 94, 82, 70, 95, 45, 90]
function determinePass(threshold, array){
return array.map(function (value) {
return value > threshold ? "pass" : "fail";
});
}
console.log(determinePass(50, score));
Or
const score = [98, 76, 94, 82, 70, 95, 45, 90]
function determinePass(threshold){
return score.map(function (value) {
return value > threshold ? "pass" : "fail";
});
}
console.log(determinePass(50));

if/else statement in map function?

I am here want to use map function in javascript to loop a type data array,but i get error for these syntax below :
function porti(scores) {
const test = scores.map(pass, fail) => {
if (scores < 75){
test.fail
} else {
test.pass
}
return {pass, fail}
}
}
output must be, if scores < 75 : fail, else : pass
console.log(porti([80, 45, 90, 65, 74, 100, 85, 30]));
// { pass: [ 80, 90, 100, 85 ], fail: [ 45, 65, 74, 30 ] }
console.log(porti([]));
// { pass: [], fail: [] }
I think reduce would be better for this situation. This will allow us to reduce the array to an object of two item arrays.
let items = [80, 45, 90, 65, 74, 100, 85, 30]
let result = items.reduce((obj, item) => {
item < 75 ? obj.fail.push(item) : obj.pass.push(item)
return obj
}, {pass:[], fail:[]})
console.log(result)
If you wanted to use filter you could...
let items = [80, 45, 90, 65, 74, 100, 85, 30]
let result = {
pass: items.filter(i => i >= 75),
fail: items.filter(i => i < 75)
}
console.log(result)
And here is how we can do it with forEach...
let items = [80, 45, 90, 65, 74, 100, 85, 30]
let result = {pass:[], fail:[]}
items.forEach(itm => itm < 75 ? result.fail.push(itm) : result.pass.push(itm))
console.log(result)
You could integrate the check as ternary for getting the key for pushing.
function porti(scores) {
var result = { pass: [], fail: [] },
score;
for (score of scores) {
result[score < 75 ? 'fail': 'pass'].push(score);
}
return result
}
console.log(porti([80, 45, 90, 65, 74, 100, 85, 30]));
console.log(porti([]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
As mentioned above .map() should best be saved for when you are looking to return an array by manipulating a previous array. If you don't wish to use a vanilla for loop. You could try this
const testScores = [...someArray of numbers]
function porti(tesScores) {
const result = {
pass: [],
fail: []
}
for (let score of testScores) {
if (score < 75) {
result.fail.push(score)
} else {
result.pass.push(score)
}
return result
}}

JS: Get RGBA value out of a string

I'm using the current code, suggested here on SO, to get the RGB values from a string like "rgb(0, 0, 0)" but also it can be "rgb(0, 0, 0,096)", so now i need to get also the alpha value
function getRGB(str) {
var match = str.match(/rgba?\((\d{1,3}), ?(\d{1,3}), ?(\d{1,3})\)?(?:, ?(\d(?:\.\d?))\))?/);
arr = [match[1], match[2], match[3]];
return arr;
}
I tried this code below but it doesn't work
function getRGBA(str) {
var match = str.match(/rgba?\((\d{1,3}), ?(\d{1,3}), ?(\d{1,3}), ?(\d{1,3})\)?(?:, ?(\d(?:\.\d?))\))?/);
arr = [match[1], match[2], match[3], match[4]];
return arr;
}
Your original regex is already allowing for the alpha (there are four sets of digits with commas between, where the fourth is optional; also note that the a in rgba near the beginning is optional because of the ? after it). You can tell whether you got the alpha by looking at match[4]:
function getRGB(str) {
var match = str.match(/rgba?\((\d{1,3}), ?(\d{1,3}), ?(\d{1,3})\)?(?:, ?(\d(?:\.\d?))\))?/);
arr = [match[1], match[2], match[3]];
if (match[4]) { // ***
arr.push(match[4]); // ***
} // ***
return arr;
}
Not strictly what you asked, but that regular expression has a few issues that will prevent it from reliably detecting strings:
It only allows one space after commas, but more than one space is valid
It doesn't allow for any spaces before commas
In the alpha part, it allows for 1. but that's invalid without a digit after the .
A couple of other notes:
That code relies on what I call The Horror of Implicit Globals because it never declares arr.
The code isn't converting the values to numbers. I don't know if you wanted to do that or not, but I figured it was worth noting.
The function throws an error if the string doesn't match the expression. Maybe that's what you want, but again I thought I'd flag it up.
This expression does a better job of matching:
/rgba?\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:\)|,\s*(\d?(?:\.\d+)?)\))/
Live Example:
function num(str) {
str = str.trim();
// Just using + would treat "" as 0
// Using parseFloat would ignore trailing invalid chars
// More: https://stackoverflow.com/questions/28994839/why-does-string-to-number-comparison-work-in-javascript/28994875#28994875
return str === "" ? NaN : +str;
}
function getRGB(str) {
const match = str.match(/rgba?\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:\)|,\s*(\d?(?:\.\d+)?)\))/);
if (!match) {
return null;
}
const arr = [
num(match[1]),
num(match[2]),
num(match[3])
];
if (match[4]) {
arr.push(num(match[4]));
}
return arr;
}
function test(str) {
console.log(str, "=>", JSON.stringify(getRGB(str)))
}
test("rgb(1,2,3,4)"); // [1, 2, 3, 4]
test("rgba(1 , 2, 3, 4)"); // [1, 2, 3, 4]
test("rgba(111 , 22, 33)"); // [111, 22, 33]
test("rgb(111, 22)"); // null (doesn't match)
test("rgb(111, 22 , 33, 1)"); // [111, 22, 33, 1]
test("rgb(111, 22, 33, 1.)"); // null (doesn't match, no digit after .)
test("rgb(111, 22, 33, 1.0)"); // [111, 22, 33, 1]
test("rgb(111, 22, 33, .5)"); // [111, 22, 33, 0.5]
.as-console-wrapper {
max-height: 100% !important;
}
And in modern JavaScript environments we could make it a bit simpler to use by using named capture groups (see the live example for the num function and why I use it rather than +/Number or parseFloat):
function getRGB(str) {
const {groups} = str.match(
/rgba?\((?<r>\d{1,3})\s*,\s*(?<g>\d{1,3})\s*,\s*(?<b>\d{1,3})\s*(?:\)|,\s*(?<a>\d?(?:\.\d+)?)\))/
) ?? {groups: null};
if (!groups) {
return null;
}
const arr = [
num(groups.r),
num(groups.g),
num(groups.b)
];
if (groups.a) {
arr.push(num(groups.a));
}
return arr;
}
Live Example:
function num(str) {
str = str.trim();
// Just using + would treat "" as 0
// Using parseFloat would ignore trailing invalid chars
// More: https://stackoverflow.com/questions/28994839/why-does-string-to-number-comparison-work-in-javascript/28994875#28994875
return str === "" ? NaN : +str;
}
function getRGB(str) {
const {groups} = str.match(
/rgba?\((?<r>\d{1,3})\s*,\s*(?<g>\d{1,3})\s*,\s*(?<b>\d{1,3})\s*(?:\)|,\s*(?<a>\d?(?:\.\d+)?)\))/
) ?? {groups: null};
if (!groups) {
return null;
}
const arr = [
num(groups.r),
num(groups.g),
num(groups.b)
];
if (groups.a) {
arr.push(num(groups.a));
}
return arr;
}
function test(str) {
console.log(str, "=>", JSON.stringify(getRGB(str)))
}
test("rgb(1,2,3,4)"); // [1, 2, 3, 4]
test("rgba(1 , 2, 3, 4)"); // [1, 2, 3, 4]
test("rgba(111 , 22, 33)"); // [111, 22, 33]
test("rgb(111, 22)"); // null (doesn't match)
test("rgb(111, 22 , 33, 1)"); // [111, 22, 33, 1]
test("rgb(111, 22, 33, 1.)"); // null (doesn't match, no digit after .)
test("rgb(111, 22, 33, 1.0)"); // [111, 22, 33, 1]
test("rgb(111, 22, 33, .5)"); // [111, 22, 33, 0.5]
.as-console-wrapper {
max-height: 100% !important;
}

Accessing a JSON integer element

I have an JSON-Object as follows:
Input for the months is
customerSend,customerReceived,totalSendAllCustomers,totalReceivedAllCustomers
var emailObj = {
"kundenNummer":17889,
"jahre":
{
2017:{
"Januar":[15,30,75,125],
"Februar":[17,32,77,127],
"März":[19,34,79,129],
},
2018:{
"Januar":[28,12,66,198],
"Oktober":[40,4,40,5],
}
}
}
How exactly do I access the specific year?
I already tried it like this:
var keysYears = Object.keys(emailObj.jahre);
var currentSelectedYear = keysYears[0];
var keysMonth = Object.keys(emailObj.jahre[currentSelectedYear]);
var currentSelectedMonth = keysMonth[0];
document.write(emailObj.jahre[currentSelectedYear].2017[0]);
I also tried some other ways of doing this but I already deleted those.
Can you tell me how to access the 2017 or 2018 data?
I know that I could convert them into strings but I want to know if I could also do it this way.
You can call the properties of your object emailObj by their names.
Either with a dot notation
emailObj.kundenNummer
Or by brackets notation
emailObj["kundenNummer"]
The dot notation won't work in your case because the name of your property is a number. You should then use
emailObj.jahre["2017"]
var emailObj = {
"kundenNummer": 17889,
"jahre": {
"2017": {
"Januar": [15, 30, 75, 125],
"Februar": [17, 32, 77, 127],
"März": [19, 34, 79, 129],
},
"2018": {
"Januar": [28, 12, 66, 198],
"Oktober": [40, 4, 40, 5],
}
}
};
let year = "2017";
let month = "Januar";
console.log(emailObj.jahre[year][month]);
You should use bracket notation.
document.write(emailObj.jahre[currentSelectedYear][currentSelectedMonth][0]);
var emailObj = {
"kundenNummer":17889,
"jahre":
{
2017:{
"Januar":[15,30,75,125],
"Februar":[17,32,77,127],
"März":[19,34,79,129],
},
2018:{
"Januar":[28,12,66,198],
"Oktober":[40,4,40,5],
}
}
}
var keysYears = Object.keys(emailObj.jahre);
var currentSelectedYear = keysYears[0];
var keysMonth = Object.keys(emailObj.jahre[currentSelectedYear]);
var currentSelectedMonth = keysMonth[0];
document.write(emailObj.jahre[currentSelectedYear][currentSelectedMonth][0]);
In a JavaScript object, the key is always a string, even if you use an integer it will be converted into a string.
obj = {
key1: //contents
key2: //contents
}
To access a specific key:
obj.key1
obj['key1']
For your example:
emailObj.jahre['2017']
emailObj['jahre']['2017']
Use the for in looping construct to loop through the keys of an object:
var emailObj = {
"kundenNummer":17889,
"jahre": {
2017:{
"Januar":[15,30,75,125],
"Februar":[17,32,77,127],
"März":[19,34,79,129],
},
2018:{
"Januar":[28,12,66,198],
"Oktober":[40,4,40,5],
}
}
}
for (key in emailObj.jahre) {
console.log(emailObj.jahre[key]) //Here key will be '2017', '2018' etc
}
You cannot access with dot notation properties which contain as name a number in JavaScript. Instead you should consider using bracket notation.
Example:
emailObj.jahre['2017']
var emailObj = {
"kundenNummer": 17889,
"jahre": {
2017: {
"Januar": [15, 30, 75, 125],
"Februar": [17, 32, 77, 127],
"März": [19, 34, 79, 129],
},
2018: {
"Januar": [28, 12, 66, 198],
"Oktober": [40, 4, 40, 5],
}
}
};
console.log(emailObj['jahre']['2017']);
console.log(emailObj.jahre['2017']);

calculate average result from multi-dimensionally sorted array (JavaScript)

Below is the layout of my JSON File.
{
"questions": ["Question1", "Question2"],
"orgs": ["Org1", "Org2", "Org3"],
"dates": ["Q1", "Q2", "Q3"],
"values": [
[
[5, 88, 18],
[50, 83, 10],
[29, 78, 80]
],
[
[46, 51, 61],
[95, 21, 15],
[49, 86, 43]
]
]
}
I'm trying to retrieve a single array of values by looping through each question, indexed by an "orgs" value and then adding each value retrieved and dividing it by data.dates.length.
Here is my code;
d3.json("data.json", function(error, data) {
var array = new Array()
var orgS = "Org2"
var org = data.orgs.indexOf(orgS);
for (var question = 0; question < data.questions.length; question++) {
array.push(
data.values[question][org]
)
console.log(array)
}
// add array together
array.reduce(function(a, b) {
return a + b;
})
// calculate average
var avg = array / data.dates.length;
})
Here is a plnk;
http://plnkr.co/edit/wMv8GmkD1ynjo9WZVlMb?p=preview
I think the issue here is how I'm retrieving the values in the first place? as at the moment, although I am retrieving the correct values in the console log, I'm getting the array twice, and both times inside nested arrays. I'm not so sure how to remedy the problem?
For reference;
[question1][org1] corresponds to the values [5, 88, 18].
Hope someone can offer some advice here?
Thanks!
Since you clarified your question to indicate you want to calculate separate averages for each question, I've rewritten my answer. You should do all the calculations in the for loop, since the loop is looping through the questions. Then store your averages in an array.
d3.json("data.json", function(error, data) {
var averages = new Array()
var orgS = "Org2"
var org = data.orgs.indexOf(orgS);
var values, sum;
for (var question = 0; question < data.questions.length; question++) {
// get the values for the question/org
values = data.values[question][org];
// calculate the sum
sum = values.reduce(function(a, b) {
return a + b;
});
// calculate the average
averages.push(sum / values.length);
}
console.log(averages);
});
Perform the .reduce() in the for loop and push that result into array. That will give you the an array of the results you expected.
array.push(data.values[question][org].reduce(function(a, b) {
return a + b
}, 0) / data.dates.length)
[
47.666666666666664,
43.666666666666664
]
Currently, you're attempting to perform addition on the arrays themselves in the .reduce() callback instead of reducing the members of each individual array to their sum, and then average.
Demo: (Click the text below to show the whole function)
var data = {
"questions": ["Question1", "Question2"],
"orgs": ["Org1", "Org2", "Org3"],
"dates": ["Q1", "Q2", "Q3"],
"values": [
[
[5, 88, 18],
[50, 83, 10],
[29, 78, 80]
],
[
[46, 51, 61],
[95, 21, 15],
[49, 86, 43]
]
]
}
x(data)
// Your callback function.
function x(data) {
var array = new Array()
var orgS = "Org2"
var org = data.orgs.indexOf(orgS);
for (var question = 0; question < data.questions.length; question++) {
array.push(data.values[question][org].reduce(function(a, b) {
return a + b
}, 0) / data.dates.length)
}
console.log(array)
}
Instead of a for loop, you could also use .map().
var array = data.questions.map(function(_, question) {
return data.values[question][org].reduce(function(a, b) {
return a + b
}, 0) / data.dates.length
})
Demo: (Click the text below to show the whole function)
var data = {
"questions": ["Question1", "Question2"],
"orgs": ["Org1", "Org2", "Org3"],
"dates": ["Q1", "Q2", "Q3"],
"values": [
[
[5, 88, 18],
[50, 83, 10],
[29, 78, 80]
],
[
[46, 51, 61],
[95, 21, 15],
[49, 86, 43]
]
]
}
x(data)
// Your callback function.
function x(data) {
var orgS = "Org2"
var org = data.orgs.indexOf(orgS);
var array = data.questions.map(function(_, question) {
return data.values[question][org].reduce(function(a, b) {
return a + b
}, 0) / data.dates.length
})
console.log(array)
}
You need to store the sum, the result of reduce.
// add array together
// store in sum
var sum = array.reduce(function(a, b) {
return a + b;
}, 0); // use 0 as start value
For the average, you do not need the length of data.dates but from array, because you collecting the values and this length is important.
// calculate average
var avg = sum / array.length;
Together for all values, you might get this
var data = { "questions": ["Question1", "Question2"], "orgs": ["Org1", "Org2", "Org3"], "dates": ["Q1", "Q2", "Q3"], "values": [[[5, 88, 18], [50, 83, 10], [29, 78, 80]], [[46, 51, 61], [95, 21, 15], [49, 86, 43]]] },
sum = [];
data.values.forEach(function (a, i) {
sum[i] = sum[i] || [];
a.forEach(function (b) {
b.forEach(function (c, j) {
sum[i][j] = sum[i][j] || 0;
sum[i][j] += c;
});
});
});
data.avg = sum.map(function (a, i) {
return a.map(function (b) {
return b / data.values[i].length;
});
});
console.log(sum);
console.log(data);

Categories

Resources