I'm trying to make a sorting algorithm visualizer using Angular. Currently doing merge sort and its giving me troubles. I am a Java programmer so I think there is something I'm doing wrong with the typescript with the recursive calls. When I run the program it is giving me errors saying 'cannot read property length of undefined'. I am lost and can't seem to put a finger on where I am going wrong.
In my first function I repeatedly split the original array called visualArr which is a global array in my program sized by the user and used for the visualization of the sorting algorithms. Then the second function I have is used for the merging of the split arrays.
performMergeSort(arr) {
if (arr.length <= 1) {
return arr;
}
const middle = Math.floor(arr.length / 2);
let left = arr.slice(0, middle);
let right = arr.slice(middle, arr.length);
left = this.performMergeSort(left);
right = this.performMergeSort(right);
return this.performMerge(left, right);
}
The timer function in the second code block is used for visuals of the sort. However, I think there is something wrong with either my logic or something I am doing wrong in TypeScript. Any feedback is appreciated.
performMerge(leftArr, rightArr) {
let lIdx = 0;
let rIdx = 0;
let i = 0;
timer(0, 100)
.pipe(takeWhile(() => leftArr.length > lIdx && rightArr.length > rIdx))
.subscribe(() => {
const lItem = leftArr[lIdx];
const rItem = rightArr[rIdx];
if (lItem > rItem) {
this.visualArr[i] = rItem;
rIdx++;
} else {
this.visualArr[i] = lItem;
lIdx++;
}
i++;
});
timer(0, 100)
.pipe(takeWhile(() => leftArr.length > lIdx ))
.subscribe(() => {
this.visualArr[lIdx] = leftArr[lIdx++];
});
timer(0, 100)
.pipe(takeWhile(() => rightArr.length > rIdx ))
.subscribe(() => {
this.visualArr[rIdx] = rightArr[rIdx++];
});
}
This error normally comes when you haven't initialized array correctly.
Try this:-
visualArr:number[]=[];
yes, you cannot read the property length of an array if it is not defined, i.e. it is not initialized, so try to initialize all your arrays:
if you know the type: myArray:string[]=[]
if not just do this: myArray=[]
Related
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);
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'm working on the FreeCodeCamp 'Random Quote Machine' front end library project using React JSX, which was working fine with the exception that it frequently produced the same quote two or three times in a row. This was not a good thing for a user to exprience.
Looking into Math.random() I quickly realisded that this was quite normal - two or three repeats of the same number in a row. This is the code I had in a React method called getQuote:
// get new quote from array
getQuote() {
let x = Math.floor(Math.random() * quotes.length);
{console.log("random num = ", x)};
this.setState(quotes[x]
);
This meant that I had to write a method of producing a random number that wasn't the same as the last. Which, thanks to the quality content on this site and input from #Etienne Martin I found pretty quickly:
Most of the answers in this thread are over complicated.
Here's a concise example of how I would do that:
function getNumber(){
return (getNumber.number = Math.floor(Math.random() * (4 + 1))) === getNumber.lastNumber ? getNumber() : getNumber.lastNumber = getNumber.number; }
console.log(getNumber()); // Generates a random number between 0 and 4
Live example: https://jsfiddle.net/menv0tur/3/ share edit flag edited
Apr 22 '17 at 16:25 answered Apr 20 '17 at 19:35
This allowed me to write my own method:
getQuote() {
const randNum = () => {
return (randNum.x = Math.floor(Math.random() * (quotes.length))) === randNum.y ? randNum() : randNum.y = randNum.x;
}
this.setState(quotes[randNum()])
}
My problem is; I don't fully understand it. Specifically getNumber.number it looks as though it is using dot notation to access a key value of our function getNumber(). I have never seen this before. As you can see in my re-write I defined the function name randNum() and I'm using randNum.x and randNum.y instead of getNumber.number and getNumber.lastNumber.
Could someone kindly enlighten me as to exactly what we are doing here?
Many thanks in advance.
The first thing you should know is that a function is an object at the same time in javascript. So I can do this:
const randNum = () => {}
randNum.property = "value";
and that's exactly what your function is doing. Probably it will be better to write it as:
const randNum = () => {
randNum.x = Math.floor(Math.random() * (quotes.length));
// I can access randNum.x and randNum.y because randNum is a function and an object
if (randNum.x === randNum.y) {
// current value is the same as older value, run the function again
return randNum();
} else {
// current value is not the same as the older value
randNum.y = randNum.x;
return randNum.y;
}
};
or, even better:
let oldValue;
const randNum = () => {
const currentValue = Math.floor(Math.random() * (quotes.length));
if (currentValue === oldValue) {
return randNum();
} else {
oldValue = currentValue;
return currentValue;
}
};
I know this is not a one line solution but is easier to understand what it does (in my opinion)
Let's rewrite getNumber function so that it's more readable:
function getNumber() {
let result;
if (
(getNumber.number = Math.floor(Math.random() * (4 + 1))) ===
getNumber.lastNumber
) {
result = getNumber(); // recursive call
} else {
result = getNumber.lastNumber = getNumber.number; // assignment expression
}
return result;
}
As you can see the function uses recursion. Also in this line: result = getNumber.lastNumber = getNumber.number; the code getNumber.lastNumber = getNumber.number is an assignment expression so result is the same as getNumber.number. Lastly it's possible to write getNumber.number because getNumber is an object. In Javascript even functions are objects. So when you call getNumber() you invoke the function, when you call getNumber.number you accessing object field.
After you define the function getNumber you can then add as many fields to it as you want for example getNumber.anotherProperty = 'whatever' then console.log(getNumber.anotherProperty) // logs 'whatever'.
You may populate your state with a shuffled version of array of quotes, popping one at a time upon nextQuote until all of them are shown, then repeat, once your shuffled array is empty, thus, the quote will not appear until all the rest are shown.
However, it does not eliminate probability of the following outcome:
quote1, quote3, quote5, quote4 | quote5, quote1, quote3..
Pushing the distance between quotes further ultimately leads you to the point where the interval of repeating for each quote is source array length, i.e. your initial array going round:
quote1, quote2... , quote5| quote1, quote2..., quote5 | quote1, quote2...
But then it won't be random quotes any longer
Live-demo of my approach, you may find as follows:
const { useState } = React,
{ render } = ReactDOM,
rootNode = document.getElementById('root')
const quotes = ['quote1', 'quote2', 'quote3', 'quote4', 'quote5'],
shuffle = arr => [...arr].reduceRight((r,_,__,s) =>
(r.push(s.splice(0|Math.random()*s.length,1)[0]), r),[])
const App = () => {
const [currentQuote, setCurrentQuote] = useState(quotes[0|Math.random()*quotes.length]),
[quoteList, setQuoteList] = useState(shuffle(quotes).filter(q => q!= currentQuote)),
nextQuote = () => {
const [newQuote, ...rest] = quoteList
!rest.length ?
setQuoteList(shuffle(quotes).filter(q => q != newQuote)) :
setQuoteList(rest)
setCurrentQuote(newQuote)
}
return (
<div>
<div>{currentQuote}</div>
<button onClick={() => nextQuote()}>Next</button>
</div>
)
}
render (
<App />,
rootNode
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
I have passed JSON from the server-side to the client-side. I have created a function to return the latest rate and currency_id. It workes for some but not workes for some.
When I use my function for product_id 1 and 2 workes well. It returns the correct array. But this not workes for product_id 3 and 4. It returns an empty array. I have mentioned the used function below.
JS function:
COMNAME.prepEdit = function (product_id) {
const currencyIds = [];
const result = [];
const sortData = COMNAME.newCurrency
.filter((item) => item.product_id === product_id)
.sort((item1, item2) => item1.id - item2.id);
for (let i = sortData.length - 1; i > 0; i--) {
const item = sortData[i];
if(!currencyIds.includes(item.currency_id)) {
currencyIds.push(item.currency_id)
result.unshift(item);
}
}
return result;
}
Please help me to find my fault here. Thank you.
I'm not too sure what you are trying to do but, shouldn't your for loop be something like this :
for (let i = sortData.length - 1; i >= 0; i--) {
Shouldn't it be i>=0 since you also want the first index?
Sorry for the awful title. I've crashed enough browsers and asking for help.
Here's the specific problem I'm trying to solve.
Create a function called 'reversedLooper' that when passed an array will loop through it backwards and subtract 2 from the last element, 1 from the second to last, 0 from to the third to last, add one to the fourth to last, add 2 to the fifth to last, etc. until it reaches the front of the array. Return the list when you are done
It's easy with [].map:
myArray.map(function(item, index, arr) {
return item + arr.length - index - 3;
});
Here is a shorter, more concise version.
function reversedLooper(arr){
var increaseBy = -2;
for(var i = arr.length - 1; i >= 0; i--){
arr[i] += increaseBy;
increaseBy++
}
return arr;
}
It's real ugly, but appears to satisfy the test cases.
var array = [1,2,3,4,5,6,7,8,9,10,11,12,13];
function reversedLooper(array) {
var newArray = [];
var n = -3;
for (var i = array.length - 1; i >= 0; i--) {
n++;
if (array[i] === array.length) {
newArray.push(array[i] - 2);
}
else if (array[i] === array.length - 1) {
newArray.push(array[i] - 1);
}
else if (array[i] === array.length - 2) {
newArray.push(array[i]);
}
else {
newArray.push(array[i] + n)
}
}
return newArray;
}
console.log(reversedLooper(array));
Building on my comment, I will use the new fiddle plug in to make something to show you here.
let list = Immutable.List.of(1,2,3,4,5)
function createListOperationsMapping(list) {
// Maybe consider dynamically creating a mapping of methods to indexes
// If you return a Map from this that is something like:
/*
Map({
0: (item) => item - 2,
1: (item) => item - 1,
// since there's a case where you subtract 0, so no change
2: (item) => item,
3: (item) => item + 1,
4: (item) => item + 2,
5: (item) => item + 3,
// etc...
})
*/
// This would allow you to map the reversedList and based on the index you could just
// call the method from the map...
// Like this (where the map from above ^^ is called listOperationMap)
/*
var mutatedReversedList = reversedList.map((item, index) => {
return listOperationMap[index](item);
})
Notice I used the word "mutated" here. If you use Immutable,
you're actually returning a copy with updates rather than mutating,
but I wanted to make it clear that the result of mapping the
Immutable List will be a new Immutable List - not the mutated
previous list. Also, if you choose to work with Immutable, remember
you will need to set new variables for return values of updates to
the Immutable data structures.
*/
}
let reversedList = list.reverse()
let reversedListSize = reversedList.size
let newList = reversedList.map((listItem, index) => {
// Do stuff in here
})
console.log(list.toJS())
console.log(reversedList.toJS())
console.log(reversedListSize)
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.7.6/immutable.min.js"></script>
Since your question was a bit vague, I'm basically just throwing out some ideas based on what there was to work with. Hopefully this is useful in at least starting to explore how you might approach the problem you are trying to solve.
Either way, I recommend taking a look at Immutable.js.
PS: If you click on "Run code snippet", then crack open your dev tools and the console.log(s) will show up in your console.