Nested for loop and alternate map method - javascript

I'm a beginner in javascript, so bear with me if this question is too simple. I'm trying to simplify this function
var calculateTotal =function(nodeData){
var totalSelectedUnit0 = 0;
var totalSelectedUnit1 = 0;
var totalSelectedUnit2 = 0;
for(x=$scope.selectFrom; x<$scope.selectTo; x++){
totalSelectedUnit0 += nodeData.items[0].usage.categories[x].current;
totalSelectedUnit1 += nodeData.items[1].usage.categories[x].current;
totalSelectedUnit2 += nodeData.items[2].usage.categories[x].current;
}
console.log(totalSelectedUnit0);
console.log(totalSelectedUnit1);
console.log(totalSelectedUnit2);
};
calculateTotal(node);
And this is how I attempted to refactor the code
var calculateTotal =function(nodeData){
var totalSelectedUnit=[];
for(i=0; i<nodeData.items.length; i++){
for(x=$scope.selectFrom; x<$scope.selectTo; x++){
totalSelectedUnit[i] += nodeData.items[i].usage.categories[x].current;
}
}
console.log(totalSelectedUnit);
};
There are couple of things I'm trying to achieve here. The calculation should ignore null or Nan values. Also I would like to use the map and reduce to achieve this calculation.

The first problem I could see is the result array is not initialized, so the result will be NaN as you are adding an undefined value to a number.
var calculateTotal = function(nodeData) {
var totalSelectedUnit = nodeData.items.map(function(item) { //create a result value for each item in the items array
return item.usage.categories.slice($scope.selectFrom, $scope.selectTo).reduce(function(v1, v2) { //sum up values between the from and to index
return v1 + (v2 || 0); //ignore the falsy values
}, 0);
})
console.log(totalSelectedUnit);
};
var $scope = {
selectFrom: 0,
selectTo: 4
};
var nodeData = {
items: [{
usage: {
categories: [2, 3, 4, 5, 6, 7, 8, 9, 1]
}
}, {
usage: {
categories: [12, 13, 14, 15, 16, 17, 18, 19, 10]
}
}, {
usage: {
categories: [22, 23, 24, 25, 26, 27, 28, 29, 20]
}
}]
};
calculateTotal(nodeData);

Related

Solution to Euler Problem 5: My code doesn't work

I have a problem with Project Euler challenge 5. The challenge is based on finding the smallest positive number that is divisible by all the numbers from 1 to 20. This is my code:
let i = 1;
function myFunction (num) {
return i % num == 0
}
while (true) {
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
if (arr.every(myFunction)) {
console.log(i)
break;
} else {
i++
continue;
}
}
The code up to number 16 works fine (720720), but once I add another number to the array, in order (16, 17, 18, ...) it doesn't work anymore. I don't know why, I'm very new to programming. If you could instruct me.
The answer above takes way to long, like he said, quite a few seconds.
This code is longer but takes less than a second to come up with the result.
const divisibleByAllToN = (n) => {
const twoToN = Array(n - 1)
.fill(2)
.map((item, index) => item + index);
let numbersToBeMultiplied = twoToN.filter((item) => {
for (var i = 2; i < item; i++) if (item % i === 0) return false;
return item > 1;
});
numbersToBeMultiplied = numbersToBeMultiplied.map((item) => {
let value = item;
while (value * item <= n) {
value = value * item;
}
return value;
});
return numbersToBeMultiplied.reduce((acc, val) => acc * val);
};
divisibleByAllToN(20);
solved :
you need to return a condition in myFunction in order for array.every to work properly :
let i = 1;
function myFunction (num) {
var result = i / num
return parseInt(result) === result // condition HERE to check if result is an int
}
while (true) {
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
if (arr.every(myFunction)) {
console.log(i)
break;
} else {
i++
continue;
}
}
doing this we get (after about 20 seconds) the right result : 232792560

Javascript function remove from position in array

I have an array:
var myArray = [12, 11, 13, 18, 30, 51, 21, 18, 20];
I need to remove each 3rd item from the array and add it to another array.
The final result should be:
var myArray = [12, 11 18, 30, 21, 18];
var secondArray = [13, 51, 20];
How can i do that?
iterate over the array and splice every 3rd element and push to the second array.
var myArray = [12, 11, 13, 18, 30, 51, 21, 18, 20];
var secondArray = [];
for (var i = 2; i < myArray.length; i += 2) {
secondArray.push(myArray[i]);
myArray.splice(i, 1);
}
console.info('myArray', myArray)
console.info('secondArray', secondArray)
I do not like example with splicing source array, because splicing is a long operation and in large arrays / multiple operations it will be not so effective. I think you should create new arrays and don't mind.
const arraysList = myArray.reduce((acc, item, index) => {
if ((index + 1) % 3 === 0) {
acc.second.push(item);
} else {
acc.first.push(item);
}
return acc;
}, { first: [], second: [] })
console.log(arraysList.first); // [12, 11 18, 30, 21, 18]
console.log(arraysList.second); // [13, 51, 20]
var myArray = [12, 11, 13, 18, 30, 51, 21, 18, 20];
var newArray1 = [];
var newArray2 = [];
myArray.forEach(function(value,index){
if((index + 1) % 3 === 0){
newArray1.push(value);
}
else {
newArray2.push(value);
}
});
console.log(newArray1); // Stores element at index 3 in the original array
console.log(newArray2); // Stores element at index other than 3 in the original array
Array#reduce the original array into 2 new arrays:
const myArray = [12, 11, 13, 18, 30, 51, 21, 18, 20];
const [without3rdItems, with3rdItems] = myArray.reduce((arrs, n, i) => {
const arr = (i + 1) % 3 ? 0 : 1;
arrs[arr].push(n);
return arrs;
}, [[], []]);
console.log(without3rdItems.join());
console.log(with3rdItems.join());
var a = [12, 11, 13, 18, 30, 51, 21, 18, 20];
var b = [ ];
var l = a.length;
for(i = 2; i < l; i += 2){
b.push(a[i]);
a.splice(i, 1);
l--;
}
console.log("Array a: ", a);
console.log("Array b: ", b);

Split javascript array of numbers into ranges

I have an array such as:
[16, 20, 1, 4, 6, 8, 9, 22, 18, 14, 13, 12]
That I would like split into 6 different arrays based on ranges 1-4, 5-8, 9-12, 13-16, 17-20, 21-24.
What is the simplest way to do this with javascript?
You could use an interval for assigning the numbers to a specific slot.
var array = [16, 20, 1, 4, 6, 8, 9, 22, 18, 14, 13, 12],
interval = 4,
result = array.reduce(function (r, a) {
var slot = Math.floor((a - 1) / interval);
(r[slot] = r[slot] || []).push(a);
return r;
}, []);
console.log(result);
The solution using Array.prototype.filter() function:
var list = [16, 20, 1, 4, 6, 8, 9, 22, 18, 14, 13, 12], i
result = [];
// considering ranges `1-4, 5-8, 9-12, 13-16, 17-20, 21-24`
for (i = 1; i < 24; i+= 4) {
result.push(list.filter(function(d){
return ((i+4 > d) && d >= i); // check if the number between lower and upper bound
}));
}
console.log(result);
Simplest answer:
var numbers = [16, 20, 1, 4, 6, 8, 9, 22, 18, 14, 13, 12];
var array1 = []; // range 1-4
var array2 = []; // range 5-8
for(var i=0; i< numbers.length; i++) {
if(numbers[i]>= 1 && numbers[i] <= 4) {
array1[i] = numbers[i]
} else if(numbers[i]>= 5 && numbers[i] <= 8) {
array2[i] = numbers[i]
}
//... continue for remaining ranges
}

1d array into binary tree

nodeArray = [ 3, 3, 7, 6, 6, 7, 15, 10, 10, 14, 13, 13, 14, 15, 23, 18, 18, 22, 21, 21, 22, 23, 0 ];
nodes = [];
links = [];
function left(i) {
return 2*i + 1;
}
function right(i) {
return 2*i + 2;
}
function parent(i) {
console.log("Parent =" + (i-1)/2);
return (i-1)/2;
}
var index = 0;
do{
if (index === 0) {
var node = {
'value': nodeArray[index],
'child1_index': left(index),
'child1_value': nodeArray[left(index)],
'child2_index': right(index),
'child2_value': nodeArray[right(index)],
'parent_index' : 'null',
'parent_value' : 'null'
};
} else {
var node = {
'value': nodeArray[index],
'child1_index': left(index),
'child1_value': nodeArray[left(index)],
'child2_index': right(index),
'child2_value': nodeArray[right(index)],
'parent_index' :parent(index),
'parent_value' : nodeArray[parent(index)],
'index' : index
};
}
nodes.push(node);
index++;
} while (index != nodeArray.length)
console.log(nodes);
I have written the above code for future turning it into a binary tree with d3.js library, unfortunately all my parent node values (which are apparently given by any nodes (index -1 )/ 2. give numbers like 5.5 etc being half the index or something. which obviously wont work. Some nodes give full integers then some do not.
example console output for one of my node objects. which looks right
Node1:
parent_index:0
parent_value:3
example of other node objects. which dont look right are
Node2:
parent_index:0.5
parent_value:undefined
Here is a jsfiddle if anyone's interested
http://jsfiddle.net/mryfw095/5/
I think you just want your parent function to round down.
function parent(i) {
console.log("Parent =" + Math.floor((i-1)/2));
return Math.floor((i-1)/2);
}

JavaScript Array Sorting/Ranking with Equal Ranks

After sorting an array of objects based on of their property values ("rating" in this case), how do you associate ranks for each object if there are ties between some of these values? Here's an example:
//Should be tied for 1st Rank
var obj1 = {
name: "Person1",
rating: 99
}
//Should be 3rd Rank
var obj2 = {
name: "Person2",
rating: 50
}
//Should be 2nd Rank
var obj3 = {
name: "Person3",
rating: 98
}
//Should be 4th Rank
var obj4 = {
name: "Person4",
rating: 0
}
//Should be tied for 1st Rank
var obj5 = {
name: "Person5",
rating: 99
}
Here's as far as I got:
var clients = [obj1, obj2, obj3, obj4, obj5];
var sorted = [];
for (var i = 0; i < clients.length; i++) {
sorted.push(clients[i]);
}
sorted.sort(function(a, b) {
return b.rating-a.rating;
});
Ultimately, I'd like to be able to get the rank using the object name, like this:
alert(sorted.indexOf(obj5) + 1);
Created a solution that worked, albeit ugly. Thanks jamie for some framework used in this:
for (var i = 0; i < clients.length; i++) {
sorted.push(clients[i]);
}
sorted.sort(function(a, b) {
return b.rating-a.rating;
});
for(var i = 0; i < sorted.length; i++) {
// original ranking
sorted[i].rank = i + 1;
}
function sortRanking() {
for (var k = 0; k < sorted.length; k++) {
for (var h = 1; h < sorted.length + 1; h++) {
if (sorted[k+h] !== undefined) {
if (sorted[k+h].tie !== true) {
if (sorted[k].rating === sorted[h + k].rating) {
sorted[k].rank = k + 1;
sorted[h + k].rank = k + 1;
sorted[k].tie = true;
sorted[h + k].tie = true;
}
}
}
}
}
}
sortRanking();
alert("Rank: " + obj3.rank);
Using ES6, here's how you can do it, adding a property rank to every client. Try the code snippet below.
function setRanks(clients) {
let currentCount = -1, currentRank = 0,
stack = 1; // consecutive clients with same rating
for (let i = 0; i < clients.length; i++) {
const result = clients[i];
if (currentCount !== result['rating']) {
currentRank += stack;
stack = 1;
} else {
stack++;
}
result['rank'] = currentRank;
currentCount = result['rating'];
}
}
// get the rank using the object name
function getRank(clientName) {
return clients.find(c => c.name === clientName)['rank'];
}
//Should be tied for 1st Rank
var obj1 = {
name: "Person1",
rating: 99
}
//Should be 3rd Rank
var obj2 = {
name: "Person2",
rating: 50
}
//Should be 2nd Rank
var obj3 = {
name: "Person3",
rating: 98
}
//Should be 4th Rank
var obj4 = {
name: "Person4",
rating: 0
}
//Should be tied for 1st Rank
var obj5 = {
name: "Person5",
rating: 99
}
var clients = [obj1, obj2, obj3, obj4, obj5];
clients.sort((c, other) => other.rating - c.rating);
setRanks(clients);
console.log(clients);
console.log(getRank('Person5'));
2nd attempt: although not quite there - i argue separating the ranking in to a different property rather than rely on the indexOf to find ranking is the way to go. You then have something clearer to manipulate when there is a tie. Still working it. Will be watching for best solution
for(var i = 0; i < sorted.length; i++) {
// original ranking
sorted[i].rank = i + 1;
}
function sortRanking() {
for(i=0; i< sorted.length; i++) {
var current = sorted[i];
var next = sorted[i + 1];
if(next === undefined || next.rating !== current.rating) {
console.log("we are done");
return "done";
}
if(next.rating === current.rating) {
for(var j = next + 1; j < sorted.length; j++) {
sorted[j].rank = sorted[j-1].rank;
}
next.rank = current.rank;
}
}
}
sortRanking();
console.log(sorted);
1st attempt - After playing around with for a bit. Here is a solution adding from your original logic:
var clients = [o1, o2, o3, o4];
var sorted = [];
for (var i = 0; i < clients.length; i++)
sorted.push(clients[i]);
sorted.sort(function (a, b) {
return clients.rating - clients.rating;
});
function checkForTieAndRating(x) {
// x parameter for object of interest
// need to get the one in front to determine if it is tied
// get index of obj of interest
var indexOfInterest = clients.indexOf(x);
var indexOfBefore = indexOfCurrent -1;
// if obj of interest is ranked #1 then return
if(indexOfBefore < 0) {
return indexOfInterest + 1;
} else {
// get the actual object before this one so you can check rating. put in variable so you can compare.
var objBefore = clients[indexOfBefore];
var ratingOfObjBefore = objBefore.rating;
if(ratingOfObjBefore === x.rating)
return "Tied for" + indexOfInterest;
}
}
// check ranking and if tie
checkForTieAndRating(obj2);
// other issue going this route - would be to then 1) alter the objects ranking following the objs that are tied - to
//Possible alternative solution: After working and about to submit it - I think it would be better to add a ranking property after the sort and manipulate the rankings from there if there are any tied.
If you want several records on the same place, you should probably use an additional immediate array, effectively grouping the elements.
I will use lodash for convinience, you should get the idea.
_.chain(clients).groupBy('rating').pairs().sortBy(0).reverse().pluck(1).value();
You loose your ability to use indexOf at this point, so you need to write your own getRank.
Again, with the help of lodash
// returns zero when no rank is found
var getRank = function(sortedArray, object) {
return 1 + _.findIndex(sortedArray, function(list) {
return _.contains(list, object);
});
};
Full working fiddle: http://jsfiddle.net/4WJN3/1/
I needed a similar piece of code for an operations scheduling script I was writing. I used objects and their properties/keys, which can have any value and can be accessed whenever needed. Also, as far as I read in some articles, the search of properties in objects can be faster than search in arrays.
The script below has three simple steps:
sort the values (ascending or descending doesn't matter for the rest of the script)
find the ranks and number of occurrences for each value
replace the given values with ranks using the data from step 2
Note! The below script will not output duplicate ranks, but instead increments ranks for duplicate values/elements.
function rankArrayElements( toBeRanked ) {
// STEP 1
var toBeRankedSorted = toBeRanked.slice().sort( function( a,b ) { return b-a; } ); // sort descending
//var toBeRankedSorted = toBeRanked.slice().sort( function( a,b ) { return a-b; } ); // sort ascending
var ranks = {}; // each value from the input array will become a key here and have a rank assigned
var ranksCount = {}; // each value from the input array will become a key here and will count number of same elements
// STEP 2
for (var i = 0; i < toBeRankedSorted.length; i++) { // here we populate ranks and ranksCount
var currentValue = toBeRankedSorted[ i ].toString();
if ( toBeRankedSorted[ i ] != toBeRankedSorted[ i-1 ] ) ranks[ currentValue ] = i; // if the current value is the same as the previous one, then do not overwrite the rank that was originally assigned (in this way each unique value will have the lowest rank)
if ( ranksCount[ currentValue ] == undefined ) ranksCount[ currentValue ] = 1; // if this is the first time we iterate this value, then set count to 1
else ranksCount[ currentValue ]++; // else increment by one
}
var ranked = [];
// STEP 3
for (var i = toBeRanked.length - 1; i >= 0; i--) { // we need to iterate backwards because ranksCount starts with maximum values and decreases
var currentValue = toBeRanked[i].toString();
ranksCount[ currentValue ]--;
if ( ranksCount[ currentValue ] < 0 ) { // a check just in case but in theory it should never fail
console.error( "Negative rank count has been found which means something went wrong :(" );
return false;
}
ranked[ i ] = ranks[ currentValue ]; // start with the lowest rank for that value...
ranked[ i ] += ranksCount[ currentValue ]; // ...and then add the remaining number of duplicate values
}
return ranked;}
I also needed to do something else for my script.
The above output has the following meaning:
index - the ID of the element in the input array
value - the rank of the element from the input array
And I needed to basically 'swap the index with the value', so that I have a list of element IDs, arranged in the order of their ranks:
function convertRanksToListOfElementIDs( ranked ) { // elements with lower ranks will be first in the list
var list = [];
for (var rank = 0; rank < ranked.length; rank++) { // for each rank...
var rankFound = false;
for (var elementID = 0; elementID < ranked.length; elementID++) { // ...iterate the array...
if ( ranked[ elementID ] == rank ) { // ...and find the rank
if ( rankFound ) console.error( "Duplicate ranks found, rank = " + rank + ", elementID = " + elementID );
list[ rank ] = elementID;
rankFound = true;
}
}
if ( !rankFound ) console.error( "No rank found in ranked, rank = " + rank );
}
return list;}
And some examples:
ToBeRanked:
[36, 33, 6, 26, 6, 9, 27, 26, 19, 9]
[12, 12, 19, 22, 13, 13, 7, 6, 13, 5]
[30, 23, 10, 26, 18, 17, 20, 23, 18, 10]
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7]
[7, 7, 7, 7, 7, 2, 2, 2, 2, 2]
[2, 2, 2, 2, 2, 7, 7, 7, 7, 7]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
rankArrayElements( ToBeRanked ):
[0, 1, 8, 3, 9, 6, 2, 4, 5, 7]
[5, 6, 1, 0, 2, 3, 7, 8, 4, 9]
[0, 2, 8, 1, 5, 7, 4, 3, 6, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[5, 6, 7, 8, 9, 0, 1, 2, 3, 4]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
convertRanksToListOfElementIDs( rankArrayElements( ToBeRanked ) ):
[0, 1, 6, 3, 7, 8, 5, 9, 2, 4]
[3, 2, 4, 5, 8, 0, 1, 6, 7, 9]
[0, 3, 1, 7, 6, 4, 8, 5, 2, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[5, 6, 7, 8, 9, 0, 1, 2, 3, 4]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
function rank(arr) {
var ret = [];
var s = [];
var i = 0;
var _key_;
for (_key_ in arr) {
var v;
v = arr[_key_];
if (!s[v]) {
s[v] = ++i;
}
ret.push( {
'Mark': v,
'Rank': s[v]
});
}
return ret;
}
var marks = [
65,
41,
38,
38,
37,
37,
92,
84,
84,
84,
83
];
marks.sort(function(a, b) {
return b-a;
});
var rank = rank(marks);
console.log(rank);
Concise, efficient, flexible.
Items with same score have same ranks, yet the next different score get a rank shifted by n (based on index). Input must be a array sorted by values of the sourceColumn. Two versions of the code, pick the one you like :
for(){ } loop
array.map()
var studentsSortedByGrades = [
{ name: "A", grade: 5 },
{ name: "B", grade: 3 },
{ name: "C", grade: 3 },
{ name: "D", grade: 2 },
];
var addRankFORLOOP = function(sortedArr,sourceColumn,newColumn){
for(var i = 0; i<sortedArr.length; i++){ //
sortedArr[i][newColumn] =
i===0 || sortedArr[i][sourceColumn] !== sortedArr[i-1][sourceColumn] ? i+1 // anytime new grade appears, rank=i
: sortedArr[i-1][newColumn] // elseIf: equal grade, then equal rank
}
return sortedArr;
};
/*//OR
var addRankMAP = function(sortedArr,sourceColumn,newColumn){
return sortedArr.map((item,i) => {
item[newColumn] = i===0 || sortedArr[i][sourceColumn] !== sortedArr[i-1][sourceColumn] ? i+1 // anytime new grade appears, rank=i
: sortedArr[i-1][newColumn] // elseIf: equal grade, then equal rank
return item; })
}; /**/
var withRanks = addRankFORLOOP(studentsSortedByGrades,'grade','rank');
console.log(withRanks) // ranks: 1,2,2,4

Categories

Resources