Javascript sort array of objects not executing - javascript

I have to sort an array of objects and I've written the code but it just doesn't execute. The array is the same and when I look in my debugger console it jumps right to the next function and doesn't execute my sort. I've tried many things but nothing seems to work. Maybe I'm overlooking something but I hope someone knows why it doesn't execute.
My code:
function orderData(toSort) {
console.dir(toSort);
toSort.sort(compareCountries);
console.dir(toSort);
return toSort;
}
function compareCountries(a, b) {
var avgx = 0;
var avgy = 0;
avgx = calcAvgForAllProperties(a, avgx);
avgy = calcAvgForAllProperties(b, avgy);
a["overalAvg"] = avgx;
b["overalAvg"] = avgy;
if (a.overalAvg > b.overalAvg)
return -1;
else if (b.overalAvg < a.overalAvg)
return 1;
else
return 0;
}
or:
function orderData(toSort) {
console.dir(toSort);
toSort.sort(function (a, b) {
var avgx = 0;
var avgy = 0;
avgx = calcAvgForAllProperties(a, avgx);
avgy = calcAvgForAllProperties(b, avgy);
a["overalAvg"] = avgx;
b["overalAvg"] = avgy;
if (a.overalAvg > b.overalAvg)
return -1;
else if (b.overalAvg < a.overalAvg)
return 1;
else
return 0;
});
console.dir(toSort);
return toSort;
}
//EDIT
Here's an example of my data:

There are several issues with your code:
The sorting function has side effects
You should not calculate overallAvg in compareCountries. Instead, you should do this before sorting.
var countries = [ /* your countries */ ];
countries.forEach(calculateCountryOverallFooAverage); // Standard forEach is not chainable.
countries.sort(compareCountriesByOverallFooAverage);
function calculateCountryOverallFooAverage(country) {
country.calculateOverallFooAverage();
}
function compareCountriesByOverallFooAverage(lhs, rhs) {
var lhsAvg = lhs.overallFooAverage();
var rhsAvg = rhs.overallFooAverage();
if (lhsAvg < rhsAvg) { return -1; }
else if(lhsAvg > rhsAvg) { return 1; }
return 0;
}
The comparison is incorrect, because the second test is equivalent with the first one (you do if (a < b) {} else if (b > a) {} else {}). It should be:
if (a.overalAvg > b.overalAvg) return -1;
else if (a.overalAvg < b.overalAvg) return 1;
else return 0;

Related

Function Fails 2 of 3 tests

I'm helping a friend write a function which will take a string of space-separated words and return the longest one, but my code fails 2 out the 3 tests used to check it.
These are the tests:
describe("test for longest function functionality", function() {
it("should return correct output for normal strings", function() {
expect(longest("A")).toEqual("A");
expect(longest("I love Avatar")).toEqual("Avatar");
expect(longest("The stupidities of youth")).toEqual("stupidities");
});
it("should return correct output for gibberish", function() {
expect(longest("hgdydrxtfEq Rradsc tstsa taeWwwec fgdd")).toEqual("hgdydrxtfEq");
});
it("should work for sentences with numbers", function() {
expect(longest("This is a sentence with a number 7685838788")).toEqual("7685838788");
});
});
This is my solution so far:
function longest(str) {
var longestStart = 0;
var longestEnd = 0;
var current = 0;
var currentLength = 0;
while (current < str.length) {
if (str.charAt(current) == ' ') {
if (currentLength > (longestEnd - longestStart)) {
longestStart = (current - currentLength);
longestEnd = current;
}
currentLength = 0;
} else {
currentLength++;
}
current++;
}
return str.substring(longestStart, longestEnd);
}
Personally I would do this by having a function like so:
function longestSubstring(stringToSplit) {
var arrayOfStrings = stringToSplit.split(" ");
var longestString = arrayOfStrings[0];
for (var i = 0; i < arrayOfStrings.length; i++) {
if (longestString.length < arrayOfStrings[i].length) {
longestString = arrayOfStrings[i];
}
}
console.log(longestString);
}
longestSubstring("Hello my friend");
longestSubstring("1 12 123 1234 12345");
longestSubstring("aaaaaaah hello yell");
You're not processing after the string ends. Try adding this code at the end too:
if (currentLength > (longestEnd - longestStart)) {
longestStart = (current - currentLength);
longestEnd = current;
}
Your code is not the simplest; you should use String.prototype.split() to achieve the code that is the easiest to understand.

Is there a javascript library that does spreadsheet calculations without the UI

I am working on a project that needs an excel like calculation engine in the browser. But, it doesn't need the grid UI.
Currently, I am able to do it by hiding the 'div' element of Handsontable. But, it isn't elegant. It is also a bit slow.
Is there a client side spreadsheet calculation library in javascript that does something like this?
x = [ [1, 2, "=A1+B1"],
[2, "=SUM(A1,A2"),3] ];
y = CalculateJS(x);
##############
y: [[1, 2, 3],
[2,3,3]]
I'm not aware of any (although I haven't really looked), but if you wish to implement your own, you could do something along these lines (heavily unoptimized, no error checking):
functions = {
SUM: function(args) {
var result = 0;
for (var i = 0; i < args.length; i++) {
result += parseInt(args[i]);
}
return result;
}
};
function get_cell(position) {
// This function returns the value of a cell at `position`
}
function parse_cell(position) {
cell = get_cell(position);
if (cell.length < 1 || cell[0] !== '=')
return cell;
return parse_token(cell.slice(1));
}
function parse_token(tok) {
tok = tok.trim();
if (tok.indexOf("(") < 0)
return parse_cell(tok);
var name = tok.slice(0, tok.indexOf("("));
if (!(name in functions)) {
return 0; // something better than this?
}
var arguments_tok = tok.slice(tok.indexOf("(") + 1);
var arguments = [];
while (true) {
var arg_end = arguments_tok.indexOf(",");
if (arg_end < 0) {
arg_end = arguments_tok.lastIndexOf(")");
if (arg_end < 0)
break;
}
if (arguments_tok.indexOf("(") >= 0 && (arguments_tok.indexOf("(") < arg_end)) {
var paren_amt = 1;
arg_end = arguments_tok.indexOf("(") + 1;
var end_tok = arguments_tok.slice(arguments_tok.indexOf("(") + 1);
while (true) {
if (paren_amt < 1) {
var last_index = end_tok.indexOf(",");
if (last_index < 0)
last_index = end_tok.indexOf(")");
arg_end += last_index;
end_tok = end_tok.slice(last_index);
break;
}
if (end_tok.indexOf("(") > 0 && (end_tok.indexOf("(") < end_tok.indexOf(")"))) {
paren_amt++;
arg_end += end_tok.indexOf("(") + 1;
end_tok = end_tok.slice(end_tok.indexOf("(") + 1);
} else {
arg_end += end_tok.indexOf(")") + 1;
end_tok = end_tok.slice(end_tok.indexOf(")") + 1);
paren_amt--;
}
}
}
arguments.push(parse_token(arguments_tok.slice(0, arg_end)));
arguments_tok = arguments_tok.slice(arg_end + 1);
}
return functions[name](arguments);
}
Hopefully this will give you a starting point!
To test in your browser, set get_cell to function get_cell(x) {return x;}, and then run parse_cell("=SUM(5,SUM(1,7,SUM(8,111)),7,8)"). It should result in 147 :)
I managed to do this using bacon.js. It accounts for cell interdependencies. As of now, it calculates values for javascript formula instead of excel formula by using an eval function. To make it work for excel formulae, all one has to do is replace eval with Handsontable's ruleJS library. I couldn't find a URI for that library... hence eval.
https://jsfiddle.net/sandeep_muthangi/3src81n3/56/
var mx = [[1, 2, "A1+A2"],
[2, "A2", "A3"]];
var output_reference_bus = {};
var re = /\$?[A-N]{1,2}\$?[1-9]{1,4}/ig
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split('');
function convertToCellRef(rows, cols) {
var alphabet_index = rows+1,
abet = "";
while (alphabet_index>0) {
abet = alphabet[alphabet_index%alphabet.length-1]+abet;
alphabet_index = Math.floor(alphabet_index/alphabet.length);
}
return abet+(cols+1).toString();
}
function getAllReferences(value) {
if (typeof value != "string")
return null;
var references = value.match(re)
if (references.length == 0)
return null;
return references;
}
function replaceReferences(equation, args) {
var index = 0;
return equation.replace(re, function(match, x, string) {
return args[index++];
});
}
//Assign an output bus to each cell
mx.forEach(function(row, row_index) {
row.forEach(function(cell, cell_index) {
output_reference_bus[convertToCellRef(row_index, cell_index)] = Bacon.Bus();
})
})
//assign input buses based on cell references... and calculate the result when there is a value on all input buses
mx.forEach(function(row, row_index) {
row.forEach(function(cell, cell_index) {
if ((all_refs = getAllReferences(cell)) != null) {
var result = Bacon.combineAsArray(output_reference_bus[all_refs[0]]);
for (i=1; i<all_refs.length; i++) {
result = Bacon.combineAsArray(result, output_reference_bus[all_refs[i]]);
}
result = result.map(function(data) {
return eval(replaceReferences(cell, data));
})
result.onValue(function(data) {
console.log(convertToCellRef(row_index, cell_index), data);
output_reference_bus[convertToCellRef(row_index, cell_index)].push(data);
});
}
else {
if (typeof cell != "string")
output_reference_bus[convertToCellRef(row_index, cell_index)].push(cell);
else
output_reference_bus[convertToCellRef(row_index, cell_index)].push(eval(cell));
}
})
})
output_reference_bus["A2"].push(20);
output_reference_bus["A1"].push(1);
output_reference_bus["A1"].push(50);

Improvising Code Into A More DRY Approach?

I am formatting an array in the function inputCategories, and am unable to correctly add a third argument of "category" - forcing me replicate the function multiple times.
Here is the current state:
Calling the function with arguments.
$scope.categories = inputCategories($scope.item.categories, $scope.settings.categories);
function inputCategories (input, settings) {
var updatedSettings = [];
angular.forEach(input, function(obj) {
updatedSettings.push({"category": obj, "ticked": true});
});
var list = updatedSettings.concat(settings);
list.sort(function(a, b) {
return (a.category > b.category) - (a.category < b.category);
});
for ( var i = 1; i < list.length; i++ ){
if(list[i-1].category == list[i].category) {
list.splice(i,1);
}
}
return list;
};
Here are the places which would require a third argument of "category".
function inputCategories (input, settings) {
var updatedSettings = [];
angular.forEach(input, function(obj) {
updatedSettings.push({****"category"****: obj, "ticked": true});
});
var list = updatedSettings.concat(settings);
list.sort(function(a, b) {
return (a.****category**** > b.****category****) - (a.****category**** < b.****category****);
});
for ( var i = 1; i < list.length; i++ ){
if(list[i-1].****category**** == list[i].****category****) {
list.splice(i,1);
}
}
return list;
};
I think that the issue I am having is because I am mixing up strings and a variable that is a string, inside of the object on the fourth line...?
Perhaps you could do something like this:
function inputCategories (input, settings, category) {
var updatedSettings = [];
angular.forEach(input, function(obj) {
var setting = { "ticked": true };
setting[category] = obj;
updatedSettings.push(setting);
});
var list = updatedSettings.concat(settings);
list.sort(function(a, b) {
return (a[category] > b[category]) - (a[category] < b[category]);
});
for ( var i = 1; i < list.length; i++ ){
if(list[i-1][category] == list[i][category]) {
list.splice(i,1);
}
}
return list;
};

What's wrong in my JS function?

I am optimizing a Java code to JS, but runs on Nashorn and do not own debug option. The input is val = "JPG ou PNG" and the output is "JPG ou PNG". Why does this happen? I need the output to be "jpg/png"
Function
function process(val) {
var cleaned = val.replaceAll("[•×\\tª°▪º⊗ fi ²●˚~ĩ`ũ]", "").trim().toLowerCase();
var out = [];
if (cleaned.contains("ou")) {
out = cleaned.split("ou");
}
else if (cleaned.contains("/")) {
out = cleaned.split("/");
}
else {
return cleaned;
}
for (var i = 0; i < out.length; i++) {
out[i] = out[i].trim();
}
return join(out, "/");
}
Three of your functions don't exist in javascript:
replaceAll(searchValue, newValue) in javascript is replace(searchValue, newValue)
contains(searchValue) in javascript is indexOf(searchValue) > -1
join(array, separator) in javascript is array.join(separator)
JSFIDDLE DEMO
Here's my solution:
function process(val) {
var cleaned = val.replace("[•×\\tª°▪º⊗ fi ²●˚~ĩ`ũ]", "").trim().toLowerCase();
var out = [];
if (cleaned.indexOf("ou") >= 0) {
out = cleaned.split("ou");
}
else if (cleaned.indexOf("/") >= 0) {
out = cleaned.split("/");
}
else {
return cleaned;
}
for (var i = 0; i < out.length; i++) {
out[i] = out[i].trim();
}
return join(out, "/");
}
Your logic was right, but strings in Javascript don't have 'replaceAll' and 'contains', so I replaced them with 'replace' and 'indexOf(x) >= 0'.
Also, you mentioned you don't have the option to debug in your environment, yet the function you provided is pretty standalone. This means you could easily copy it into another environment to test it in isolation.
For example, I was able to wrap this code in a HTML file then open it in my web browser (I had to implement my own 'join').
<html>
<body>
<script>
function join(val, divider) {
var out = "";
for(var i = 0; i < val.length; i++) {
if(out.length > 0) out += divider;
out += val[i];
}
return out;
}
function process(val) {
var cleaned = val.replace("[•×\\tª°▪º⊗ fi ²●˚~ĩ`ũ]", "").trim().toLowerCase();
var out = [];
if (cleaned.indexOf("ou") >= 0) {
out = cleaned.split("ou");
}
else if (cleaned.indexOf("/") >= 0) {
out = cleaned.split("/");
}
else {
return cleaned;
}
for (var i = 0; i < out.length; i++) {
out[i] = out[i].trim();
}
return join(out, "/");
}
var inval = "JPG ou PNG";
var outval = process(inval);
console.log(inval + " => " + outval);
</script>
</body>
</html>
I verified it works by opening up the console and seeing the output "JPG ou PNG => jpg/png".

I can't figure out why it's saying that the matcher function is undefined.

This code is designed to identify an array of anagrams for a string given an array of possible anagrams.
var anagram = function(input) {
return input.toLowerCase();
}
I'm adding the matcher function here to the String prototype.
String.prototype.matcher = function(remainingLetters) {
var clone = this.split("");
for (var i = 0; i < clone.length; i++) {
if (clone[i].indexOf(remainingLetters) > -1) {
remainingLetters.splice(clone[i].indexOf(remainingLetters, 1));
clone.splice(i, 1);
}
}
if (remainingLetters.length == 0 && clone.length == 0) {
return true;
}
else {
return false;
}
}
a
String.prototype.matches = function(matchWordArray) {
var result = [];
for (var i = 0; matchWordArray.length; i++) {
var remainingLetters = this.split("");
if (matchWordArray[i].matcher(remainingLetters)) {
result.push(arrayToMatch[i]);
}
}
return result;
}
var a = anagram("test");
a.matches(["stet", "blah", "1"]);
module.exports = anagram;
Should probably be:
for (var i = 0; i < matchWordArray.length; i++) {
The original statement:
for (var i = 0; matchWordArray.length; i++) {
...would result in an infinite loop because matchWordArray.length is always truthy (3) in your test.

Categories

Resources