Reputation: 23
My app is retrieving json data. The json file have almost one thousand words with a strucuture like this:
{"THEMES":{"THEME1":["ITEM1","ITEM2","ITEM3"],"THEME2":["ITEM1",...]...}}
The file is about 25kb. At a certain point of my application I need to compare which ITEM is related to which THEME to generate inside an angular ng-repeat a selected item related to a word. This is the part of my angular which handles these:
<div class="well">
<div class="input-group" bindonce ng-repeat="word_in_list in words_list">
<div class="form-group">
<select>
<option ng-selected="word_in_list.select == theme" ng-repeat="theme in themes" value="{{theme}}">{{theme}}</option>
</select>
</div>
<div class="form-group">
<input type="text" class="form-control" placeholder="{{word_in_list.input}}">
<span class="input-group-addon">
<input type="checkbox" ng-click="listControlWord($event, word_in_list.input, word_in_list.select)">
</span>
</div>
</div>
</div>
The important part is this one that follows:
$http.get('json/word_bank.json')
.success(function (result) {
$scope.themes = Object.keys(result.TEMAS);
for (var i = 0, z = $scope.themes.length; i < z; i++) {
for (var j = 0; j < result.TEMAS[$scope.themes[i]].length; j++) {
$scope.words_list.push({select: $scope.themes[i], input: result.TEMAS[$scope.themes[i]][j]});
}
}
});
The issue is that the browser takes about two minutes to render the information and it often crashes the browser. The loop is working fine and info is being retrieved ok, it just is the time it takes that is not acceptable. How can I optimize these loops?
Upvotes: 2
Views: 442
Reputation: 23
Well. I´ve managed to solve this issue paginating my application. I got the code from here: Code to tune long lists in AngularJs
Hope this help somebody else. Best.
Upvotes: 0
Reputation: 4194
Your algorithm is O(N^2), and aside from domain specific heuristics, there's no way around that really. That being said, you can look at this differently as you implied. Really you're trying to concatenate arrays, if I understand the problem correctly.
Here is a performance test for what you're trying to accomplish.
Unfortunately I don't think you can use the most efficient method (leveraging apply
), but you could use some combination of these techniques. By concatenating all the arrays, and keeping some metadata about what indexes in the concatenated array tie to what "keys", you can reduce this to O(N).
Something like...
<!doctype html>
<html lang="en">
<head>
<title>Test</title>
<script src="d3.js" charset="utf-8"></script>
</head>
<body>
<div id="divPerfTest"></div>
<script>
var result = {"THEMES":{"THEME1": ["ITEM1","ITEM2","ITEM3"],"THEME2":["ITEM1","ITEM2"]}};
var keys = Object.keys(result.THEMES),
master = [],
masterBreaks = [],
themeNames = [],
word_list = [],
theme, idx, key,
breakIdx = 0,
breakOffset = 0,
outStr = "";
for(idx = 0; idx < keys.length; idx++) {
key = keys[idx];
theme = result.THEMES[key];
master = master.concat(theme);
masterBreaks.push(theme.length);
themeNames.push(key);
}
masterBreaks.push(master.length); // need the last break to exceed last index
for (idx = 0; idx < master.length; idx++) {
if (idx >= masterBreaks[breakIdx] + breakOffset) {
breakOffset += masterBreaks[breakIdx++];
}
word_list.push({
select: themeNames[breakIdx],
input: master[idx]
});
}
for(idx=0; idx<word_list.length; idx++) {
outStr += "[ " + idx.toString() + " ] Select: " + word_list[idx].select + ", Input: " + word_list[idx].input + "<br/>";
}
document.querySelector("#divPerfTest").innerHTML = outStr;
</script>
</body>
</html>
However, I think a better approach would be to change your data schema. Instead of having variables names defining different themes, is it possible to either restructure or massage the data such that the name of the theme is an attribute of an object, and list of items is another attribute of that same object. Then you maintain a list of those objects...
{ THEMES: [
{ id: "THEME1",
data: ["ITEM1", "ITEM2"] },
{ id: "THEME2",
data: ["ITEM1", "ITEM2", "ITEM3"] }
}
Upvotes: 2
Reputation: 624
Since you are just looping through an array you can split that up into different parts and use timers to process each part in iterations instead of the whole thing at once.
Check this link out: http://ejohn.org/blog/how-javascript-timers-work/
Upvotes: 0