I've been trying to find a solution, but I don't know what's wrong here.
The code below results in this error on the line with the while statement:
Uncaught TypeError: Cannot read properties of undefined (reading '1')
I want to make a while loop, such that while two elements in an array are the same, that value is pushed in the result array until the compared elements are different:
function calcularModa(listaUser){
const lista = listaUser;
const listaCount = {};
lista.map(
function (elemento){
if (listaCount[elemento]){
listaCount[elemento] += 1;
} else{
listaCount[elemento] = 1;
}
}
);
const listaArray = Object.entries(listaCount).sort(
function (valorAcumulado, nuevoValor) {
return nuevoValor[1] - valorAcumulado[1];
}
);
let moda;
if (listaArray[0][1] != listaArray[1][1]){
moda = listaArray[0];
return moda;
}
moda = [listaArray[0]]
let i = 1;
while(listaArray[0][1] == listaArray[i][1])
{
moda.push(listaArray[i])
i++;
}
return moda;
}
let moda = calcularModa([1,1,2,2,3,3]);
console.log(moda);
Your final loop is accessing listaArray[i][1] without first checking that i is still less than the length of that array. So add i < listaArray.length as a condition in that while loop.
Not your question, but:
There is no reason to treat a separate case in the if that precedes that loop. Just remove that part: the while loop should take care of it.
Don't use .map when you just want to iterate and not really map. A for..of loop would be appropriate here.
As your input seems to consist of numbers, you need to make the conversion from string back to number again. Object keys are never of the number type.
Your code could look like this (still with the while loop):
function calcularModa(listaUser) {
const lista = listaUser;
const listaCount = {};
for (let elemento of lista) {
if (listaCount[elemento]) {
listaCount[elemento] += 1;
} else {
listaCount[elemento] = 1;
}
}
const listaArray = Object.entries(listaCount).sort(
function(valorAcumulado, nuevoValor) {
return nuevoValor[1] - valorAcumulado[1];
}
);
let i = 1;
while (i < listaArray.length && listaArray[0][1] == listaArray[i][1]) {
i++;
}
return listaArray.slice(0, i).map(([key]) => key).map(Number);
}
let moda = calcularModa([1,2,4,3,2,3,5]);
console.log(moda);
Related
I am learning the fundamentals of JavaScript currently but am realizing there are definite gaps in my knowledge. I recently started attempting challenges on Codewars when this issue became much more apparent. My latest struggle has been attempting to get this 'for loop' to push characters into an array of numbers in order to format it like a phone number. As many different solutions as I have tried, none of them actually do what I am trying to accomplish. Any help figuring out exactly where I'm going wrong here and what holes are in my logic would be appreciated. My best attempt is this:
const createPhoneNumber = (phoneNumber) => {
let formattedNumber = [];
formattedNumber.push(phoneNumber)
for (let i = 0; i < formattedNumber.length; i++) {
if (formattedNumber[i] === 0) {
formattedNumber.push('(')
}
if (formattedNumber[i] === 2) {
formattedNumber.push(')')
}
if (formattedNumber[i] === 5) {
formattedNumber.push('-')
}
}
return(formattedNumber.toString());
}
console.log(createPhoneNumber(1234567890));
Some feedback:
You're inserting one item into the array formattedNumber.push(phoneNumber) then looping through it, so there's only one iteration
Instead, you could convert the number to a string and iterate using its length
The check formattedNumber[i] === 0 is comparing the value to 0 (this check fails and is why your function is returning the unformatted phone number) but you want to compare the index, so change this to i === 0
At the end of the function you're using toString() to join the characters back together but this will include commas between values, instead use .join('')
const createPhoneNumber = (phoneNumber) => {
const phoneNumberStr = (phoneNumber).toString();
let formattedNumber = [];
for (let i = 0; i < phoneNumberStr.length; i++) {
if (i === 0) {
formattedNumber.push('(')
}
if (i === 2) {
formattedNumber.push(')')
}
if (i === 5) {
formattedNumber.push('-')
}
formattedNumber.push(phoneNumberStr[i]);
}
return(formattedNumber.join(''));
};
console.log(createPhoneNumber(1234567890))
Also, you can use .reduce() to achieve the same thing, it's a convenient function that iterates through an array, and passes a value from one iteration to the next:
const createPhoneNumber = (phoneNumber) =>
(phoneNumber).toString().split('').reduce((acc, char, i) => {
let pre = '';
if (i == 0) { pre = '('; }
if (i == 2) { pre = ')'; }
if (i == 5) { pre = '-'; }
return `${acc}${pre}${char}`;
}, '');
console.log(createPhoneNumber(1234567890));
BTW, I think your question was downvoted because you didn't provide an expected output or more details of the error 😉
I am doing an exercise on JS Hero website:
Write a function add that takes a string with a summation task and returns its result as a number. A finite number of natural numbers should be added. The summation task is a string of the form '1+19+...+281'.
Example: add('7+12+100') should return 119.
The code I have written is as follows:
function add (string) {
let partsArray = string.split("+");
let added = parseInt(partsArray[0]);
for (let i=0; i<=partsArray.length; i++) {
added = added + parseInt(partsArray[i]);
}
return added;
}
It returns NaN. Any ideas how to solve this one?
You were going out of bounds on your array. Also you should just initialize the added to 0 as you start looking at the array from index 0. Note I added some console.logs to give you an idea of how you might debug something like this.
function add (string) {
let partsArray = string.split("+");
console.log("parts", partsArray);
let added = 0;
for (let i=0; i<partsArray.length; i++) {
console.log("i",parseInt(partsArray[i]));
added += parseInt(partsArray[i]);
}
return added;
}
If you add the <= back and run the code with the console.logs you will see in console the following. Note with the <= you have 4 indexes rather than the expected 3. This is because the size is 3 but the array is indexed from zero. When you use < you get the expected answer.
You could also use the reduce method:
function add(string) {
return string.split('+').reduce((accumulator, currentValue) => accumulator +
parseInt(currentValue, 10),0)
}
If you still want to start with the first index ..you can do it like below
function add (string) {
let partsArray = string.split("+");
let added = parseInt(partsArray[0]);
for (let i=1; i<partsArray.length; i++) {
added += parseInt(partsArray[i]);
}
return added;
}
function add(input) {
let myinput = input.split("+") //split your value
let sum = 0;
for (let i = 0; i < myinput.length; i++) {
sum = sum + +myinput[i]; //use + for identify the number value
}
return sum;
}
The simplest possible answer is:
function add(str){
return eval(str)
}
My sumUpItems function works well. However, I am having problems with adding up items into basket. The second function isn't working. What I am doing wrong?
sumUpItems() {
let sum = 0;
for (const item of this.basketItems) {
sum += Number.parseFloat(item.price);
}
return sum;
},
basketCount() {
let count = 0;
for (let item of this.basketItems) {
count = ++item;
}
return count;
}
}
sumUpItems()
You can simplify the sumUpItems by using the reduce function which you can call on an array this.basketItems.
The most short way is:
sumUpItems() {
return this.basketItems.reduce((totalPrice, basketItem) => totalPrice + basketItem.price, 0);
}
What the reduce function does is it executes a reducer function on each element of the array, resulting in single output value, in this case the totalPrice of all basketItems.
if you are more comfortable with brackets you could write it down like this:
sumUpItems() {
return this.basketItems.reduce((totalPrice, basketItem) => {
return totalPrice + basketItem.price;
}, 0);
}
Or in ES5 javascript (without arrow function):
sumUpItems() {
return this.basketItems.reduce(function(totalPrice, basketItem) {
return totalPrice + basketItem.price;
}, 0);
}
I recommend you to rename the sumUpItems() function name to getTotalPrice() or something like that, it describes more specifically what the function does and returns.
BasketCount()
The reason why basketCount() is not returning the correct amount of items is because the incrementation of the variable count is invalid. let item holds the object value of the array item inside this.basketItems and you are trying to add this object to a number.
What you can do is return the length of the this.basketItems directly by writing:
basketCount() {
return this.basketItems.length;
}
Extra information
Reduce function documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
There are some powerfull methods that you can call on an array.
Most commonly the map, filter and reduce method. The reduce method is a bit tricky when you start using them but they can be handy in cases like this.
I recommend you to read this article on medium: https://medium.com/poka-techblog/simplify-your-javascript-use-map-reduce-and-filter-bd02c593cc2d or search on google for map,filter and reduce javascript arrays, it will help you a lot ;)
Check the code very well, in basketCount() function the count variable is not incrementing and besides you increment item by 1 before assigning its value into count;
Are you planning doing something like this;
count += ++item;
or
count += item;
It will be better if you can include code for basketItems also;
If you rewrote this section like so: it will work
basketCount() {
let count = 0;
for (let item of this.basketItems) {
++count;
}
return count;
}
But a better way of doing this will do like so:
let d = 0;
let p = arr.map(eachone => d++); //p contains your count
Another option using forEach:
let arr = [1,2,3,4,5]; //replace arr with this.baskets
let b = 0;
arr.forEach((eachone) => {
b++
});
LASTLY: if you dont want to create an unused variable:
let baskets = [1,2,3,4,5];
let p = 0;
for(i=0; i<=baskets.length; i++) {
p++;
}
return p;
Welcome to stackover flow :)
I'm sure I could eventually figure this out, but the documentation is a bit verbose and I think this ought to be a common question about asm.js.
Here goes:
Suppose that I have a function that looks like this:
function computeSquares() {
var i, array = [];
for(i = 0; i < 100; i++) {
array.push(i * i);
}
return array;
}
The above function computes the square of the integers from 0 to 99 and returns an array containing the results.
I would like to write a function like this in asm.js that returns an object containing the 100 square values. If I've read the documentation, I'm supposed to use the ArrayBuffer object, but I'm a bit confused about how to do it.
Please illuminate me by giving an example of how to code this with asm.js.
You can only return doubles, signed ints and voids from exported functions. If you want to return an array you'll have to write it into the heap.
The module would look like this:
function squaresModule(stdlib, foreign, heap) {
"use asm";
var imul = stdlib.Math.imul;
var array = new stdlib.Uint32Array(heap);
function compute( max ){
max = max|0; //max is an integer
var i = 0;
for( i = 0; (i|0) < (max|0); i = (i+1)|0 ) {
array[ i <<2>>2 ] = imul(i, i)|0;
}
return 0; //asm functions have to return a number
}
return {compute:compute};
}
.
Then use execute and log the array:
var array = Uint32Array( 100 );
var module = squareModule(
{Math:Math,Uint32Array:Uint32Array}, {}, array.buffer
);
module.compute(100);
console.log(array);
Instead of returning an array, I write the results in a convenient representation inside asm.js and then extract it with a wrapper function in JS that repeatedly calls into asm.js code to get the next value:
var result = [];
var i = 0;
while (true) {
i = return_next_array_value_from_asmjs();
if (i !== 0) {
result.push(i);
} else {
break;
}
}
The downside is that you need to reserve at least one value as a stop marker. I have not tested the performance because I couldn't get any other method to work.
I've got an in page text search using JS, which is here:
$.fn.eoTextSearch = function(pat) {
var out = []
var textNodes = function(n) {
if (!window['Node']) {
window.Node = new Object();
Node.ELEMENT_NODE = 1;
Node.ATTRIBUTE_NODE = 2;
Node.TEXT_NODE = 3;
Node.CDATA_SECTION_NODE = 4;
Node.ENTITY_REFERENCE_NODE = 5;
Node.ENTITY_NODE = 6;
Node.PROCESSING_INSTRUCTION_NODE = 7;
Node.COMMENT_NODE = 8;
Node.DOCUMENT_NODE = 9;
Node.DOCUMENT_TYPE_NODE = 10;
Node.DOCUMENT_FRAGMENT_NODE = 11;
Node.NOTATION_NODE = 12;
}
if (n.nodeType == Node.TEXT_NODE) {
var t = typeof pat == 'string' ?
n.nodeValue.indexOf(pat) != -1 :
pat.test(n.nodeValue);
if (t) {
out.push(n.parentNode)
}
}
else {
$.each(n.childNodes, function(a, b) {
textNodes(b)
})
}
}
this.each(function() {
textNodes(this)
})
return out
};
And I've got the ability to hide columns and rows in a table. When I submit a search and get the highlighted results, there would be in this case, the array length of the text nodes found would be 6, but there would only be 3 highlighted on the page. When you output the array to the console you get this:
So you get the 3 tags which I was expecting, but you see that the array is actually consisting of a [span,undefined,span,undefined,undefined,span]. Thus giving me the length of 6.
<span>
<span>
<span>
[span, undefined, span, undefined, undefined, span]
I don't know why it's not stripping out all of the undefined text nodes when I do the check for them. Here's what I've got for the function.
performTextSearch = function(currentObj){
if($.trim(currentObj.val()).length > 0){
var n = $("body").eoTextSearch($.trim(currentObj.val())),
recordTitle = "matches",
arrayRecheck = new Array(),
genericElemArray = new Array()
if(n.length == 1){
recordTitle = "match"
}
//check to see if we need to do a recount on the array length.
//if it's more than 0, then they're doing a compare and we need to strip out all of the text nodes that don't have a visible parent.
if($(".rows:checked").length > 0){
$.each(n,function(i,currElem){
if($(currElem).length != 0 && typeof currElem != 'undefined'){
if($(currElem).closest("tr").is(":visible") || $(currElem).is(":visible")){
//remove the element from the array
console.log(currElem)
arrayRecheck[i] = currElem
}
}
})
}
if(arrayRecheck.length > 0){
genericElemArray.push(arrayRecheck)
console.log(arrayRecheck)
}
else{
genericElemArray.push(n)
}
genericElemArray = genericElemArray[0]
$("#recordCount").text(genericElemArray.length + " " +recordTitle)
$(".searchResults").show()
for(var i = 0; i < genericElemArray.length; ++i){
void($(genericElemArray[i]).addClass("yellowBkgd").addClass("highLighted"))
}
}
else{
$(".highLighted").css("background","none")
}
}
If you look at the code below "//check to see if we need to do a recount on the array length. ", you'll see where I'm stripping out the text nodes based off of the display and whether or not the object is defined. I'm checking the length instead of undefined because the typeof == undefined wasn't working at all for some reason. Apparently, things are still slipping by though.
Any idea why I'm still getting undefined objects in the array?
My apologies for such a big post!
Thanks in advance
I've modified your eoTextSearch() function to remove dependencies on global variables in exchange for closures:
$.fn.extend({
// helper function
// recurses into a DOM object and calls a custom function for every descendant
eachDescendant: function (callback) {
for (var i=0, j=this.length; i<j; i++) {
callback.call(this[i]);
$.fn.eachDescendant.call(this[i].childNodes, callback);
}
return this;
},
// your text search function, revised
eoTextSearch: function () {
var text = document.createTextNode("test").textContent
? "textContent" : "innerText";
// the "matches" function uses an out param instead of a return value
var matches = function (pat, outArray) {
var isRe = typeof pat.test == "function";
return function() {
if (this.nodeType != 3) return; // ...text nodes only
if (isRe && pat.test(this[text]) || this[text].indexOf(pat) > -1) {
outArray.push(this.parentNode);
}
}
};
// this is the function that will *actually* become eoTextSearch()
return function (stringOrPattern) {
var result = $(); // start with an empty jQuery object
this.eachDescendant( matches(stringOrPattern, result) );
return result;
}
}() // <- instant calling is important here
});
And then you can do something like this:
$("body").eoTextSearch("foo").filter(function () {
return $(this).closest("tr").is(":visible");
});
To remove unwanted elements from the search result. No "recounting the array length" necessary. Or you use each() directly and decide within what to do.
I cannot entirely get my head around your code, but the most likely issue is that you are removing items from the array, but not shrinking the array afterwards. Simply removing items will return you "undefined", and will not collapse the array.
I would suggest that you do one of the following:
Copy the array to a new array, but only copying those items that are not undefined
Only use those array items that are not undefined.
I hope this is something of a help.
Found the answer in another post.
Remove empty elements from an array in Javascript
Ended up using the answer's second option and it worked alright.