I have an array:
const arr1 = [
"Strongly positive",
"Positive",
"Neutral",
"Negative",
"Strongly negative"
];
and responses object:
const responses = [
{ values: { QID16: 3 } },
{ values: { QID16: ["1", "2", "3"] } },
{ values: { QID16: 1 } }
];
The goal is to create a function that returns a map.
The map's keys are the elements of arr1 and the values are the count of those elements if their index + 1
appears in the responses object.
For example, responses[0].values.QID16
is 3 which is Neutral. (Neutral has index 2)
The problem is when the values in responses is an array like in responses[1]
I created the following function:
function getCounts(mainId, choices, responses) {
let choicesCounts = new Map();
choices.forEach((choice, i) => {
choicesCounts.set(choice, 0);
const id = i + 1;
responses.forEach((response) => {
if (response.values[mainId] && response.values[mainId] === id) {
choicesCounts.set(choice, choicesCounts.get(choice) + 1);
}
if (
response.values[mainId] &&
Array.isArray(response.values[mainId]) &&
response.values[mainId].includes(id.toString())
) {
// this is the part where I need help
response.values[mainId].forEach((n) => {
choicesCounts.set(
choices.at(parseInt(n, 10) - 1),
choicesCounts.get(choices.at(parseInt(n, 10) - 1)) + 1
);
});
}
});
});
return choicesCounts;
}
It would be called like this:
console.log(getCounts("QID16", arr1, responses));
Desired output:
// desired output is a map not an object
const desiredOutput = {
"Strongly positive": 2, // number 1 appears twice in responses
Positive: 1, // number 2 appers once in responses
Neutral: 2,
Negative: 0,
"Strongly negative": 0
};
It works in the case where the values are numbers but not when they're arrays.
What is wrong with this function? Any suggestions to make it simpler?
The OP should think about choosing an approach which breaks the entire task apart into smaller ones.
From the ratings-array (the one which literally names the ratings) create a rating-value (the ones that come with the response item's values
) based map for looking up rating-keys (the rating-names which are the keys to the to be achieved rating-counts map).
const ratingValuesToKeys = new Map([
"Strongly positive",
"Positive",
"Neutral",
"Negative",
"Strongly negative",
].map((key, idx) => [idx + 1, key]));
console.log(Object.fromEntries([...ratingValuesToKeys]));
Also from the very same ratings-array create a rating-name based map for counting/summing up the occurrences of related rating-values.
const ratingCounts = new Map([
"Strongly positive",
"Positive",
"Neutral",
"Negative",
"Strongly negative",
].map(key => [key, 0]));
console.log(Object.fromEntries([...ratingCounts]));
Sanitize and collect all key specific rating-values for they are occurring as different types like number and/or string values as well as arrays.
const ratingValues = [
{ values: { QID16: 3 } },
{ values: { QID16: ["1", "2", "3"] } },
{ values: { QID16: 1 } },
]
.reduce((result, { values }) => {
if (values.hasOwnProperty('QID16')) {
const rating = values['QID16'];
if (Array.isArray(rating)) {
result
.push(
...rating
.map(value =>
parseInt(value, 10)
)
);
} else {
result
.push(parseInt(rating, 10));
}
}
return result;
}, []);
console.log({ ratingValues });
Based on all sanitized key specific rating-values do update each rating's occurrence by incrementing the related rating-key's count-value.
The final combined implementation and example code then might look similar to this ...
const ratings = [
"Strongly positive",
"Positive",
"Neutral",
"Negative",
"Strongly negative",
];
const responses = [
{ values: { QID16: 3 } },
{ values: { QID16: ["1", "2", "3"] } },
{ values: { QID16: 1 } },
];
function getRatingCounts(responseValueKey, ratings, responses) {
// create a rating-value based map for looking up rating-keys.
const ratingValuesToKeys = new Map(
ratings
.map((key, idx) => [idx + 1, key])
);
// create a rating-key based map for counting/summing up ratings.
const ratingCounts = new Map(
ratings
.map(key => [key, 0])
);
// sanitize and collect all key specific rating-values
// for they are occurring as different types ... like
// number and/or string values as well as arrays.
const ratingValues = responses
.reduce((result, { values }) => {
if (values.hasOwnProperty(responseValueKey)) {
const rating = values[responseValueKey];
if (Array.isArray(rating)) {
result
.push(
...rating
.map(value =>
parseInt(value, 10)
)
);
} else {
result
.push(parseInt(rating, 10));
}
}
return result;
}, []);
// based on all sanitized key specific rating-values
// do update each rating's occurrence by incrementing
// the related rating-key's count-value.
ratingValues
.forEach(value => {
const ratingKey = ratingValuesToKeys.get(value);
const ratingCount = ratingCounts.get(ratingKey);
ratingCounts.set(ratingKey, ratingCount + 1);
});
return ratingCounts;
}
const ratingCounts = getRatingCounts('QID16', ratings, responses);
console.log({
ratingCounts,
'counts as entry list': [...ratingCounts],
'counts as object': Object.fromEntries([...ratingCounts]),
});
.as-console-wrapper { min-height: 100%!important; top: 0; }