Skip to content

Instantly share code, notes, and snippets.

@atronov
Forked from verkholantsev/shri.js
Last active August 29, 2015 14:25
Show Gist options
  • Select an option

  • Save atronov/fbd7e728b78606a3f5ee to your computer and use it in GitHub Desktop.

Select an option

Save atronov/fbd7e728b78606a3f5ee to your computer and use it in GitHub Desktop.
window.addEventListener("load", function() {
var resultMessage = "";
var defaultName = "Japan";
var name = defaultName; // по умолчанию будет выбрано Japan
var askPopulation = function() {
var message = (resultMessage && resultMessage+"\n") + "Put country or city name";
// Я бы никогда не стал использовать prompt, но так написано в задании =)
name = window.prompt(message, name);
if (name && name.trim().length > 0) {
name = name.trim();
getCityOrCountryPopulation(name, function(error, result) {
if (error) {
resultMessage = "Error happens: " + error;
console.error(error);
} else {
resultMessage = [
(result.city)? "Population of city " + name + " - " + result.city: null,
(result.country)? "Population of country " + name + " - " + result.country: null]
.filter(function(line) { return line; })
.join("/n");
if (resultMessage.length === 0) {
resultMessage = "No country nor city with name " + name;
}
}
// если пользователь ввёл что-то то выводим результат сразу
askPopulation();
});
} else {
name = defaultName;
resultMessage = "You put nothing. Try again.";
// если ничено не вводил или нажал Cancel, ждём 4 секунды и позволяем закрыть эту ужасную страницу
setTimeout(askPopulation, 4000);
}
};
askPopulation();
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ШРИ - Задаие 2</title>
<script src="shri.js" type="application/javascript"></script>
<script src="app.js" type="application/javascript"></script>
</head>
<body>
<!-- Не нужно контента, всё через window.prompt -->
</body>
</html>
/**
* Реализация API, не изменяйте ее
* @param {string} url
* @param {function} callback
*/
function getData(url, callback) {
var RESPONSES = {
'/countries': [
{name: 'Cameroon', continent: 'Africa'},
{name :'Fiji Islands', continent: 'Oceania'},
{name: 'Guatemala', continent: 'North America'},
{name: 'Japan', continent: 'Asia'},
{name: 'Yugoslavia', continent: 'Europe'},
{name: 'Tanzania', continent: 'Africa'}
],
'/cities': [
{name: 'Bamenda', country: 'Cameroon'},
{name: 'Suva', country: 'Fiji Islands'},
{name: 'Quetzaltenango', country: 'Guatemala'},
{name: 'Osaka', country: 'Japan'},
{name: 'Subotica', country: 'Yugoslavia'},
{name: 'Zanzibar', country: 'Tanzania'}
],
'/populations': [
{count: 138000, name: 'Bamenda'},
{count: 77366, name: 'Suva'},
{count: 90801, name: 'Quetzaltenango'},
{count: 2595674, name: 'Osaka'},
{count: 100386, name: 'Subotica'},
{count: 157634, name: 'Zanzibar'}
]
};
setTimeout(function () {
var result = RESPONSES[url];
if (!result) {
return callback('Unknown url');
}
callback(null, result);
// этот код нельзя менять, но тут бага: Math.random это функция, а не свойство
// Math.round(Math.random * 1000) === NaN
// setTimeout воспринимает NaN, как 0
}, Math.round(Math.random() * 1000));
}
/**
* Ваши изменения ниже
*/
// 1) ошибка происходит из-за переменной request
// описал подробно ниже;
// 2) также проблемы могут возникнуть с использованием переменных в глобальной области видимости (без var)
// i, l, K, j,
// можно было избежать с помощью use strict
// и все другие переменные, которые даже c var но вне функции, будут помещаться в глобальную область видимости,
// если не оборачивать этот код в функцию;
// 3) в API предусмотрена передача ошибки, но в методе она никак не использовалась;
// 4) я бы ещё нормальные имена переменным дал =)
// этот метод я специально не менял, а добавил новые для страны и города ниже
/**
* Get population of country
* @param {string} continent
* @param {function} onPopulation
*/
function getContinentPopulation(continent, onPopulation) {
var requests = ['/countries', '/cities', '/populations'];
var responses = {};
for (var i = 0; i < 3; i++) {
// тело for не создаёт новой области видимости
// и всегда будет использован один экземпляр переменной request
// в каждом вызове callback всегда будет request="/populations",
// т.к. API работает асинхронно, то все итерации for будут выполнены до первого вызова callback
// нужно создавать новую переменную для каждого вызова callback
// например через bind
// появиться такая ошибка могла из-за того, что разработчик не учел, что облостью видимости
// в JS является всегда функция, так обычно пологают разработчики других ЯП, например Java,
// избежать ошибки можно было не используя оределение функции в теле for, а определить отдельную функцию
// в таком случае нельзя было бы сослаться на переменную request
// в ES6 такая проблема может решаться ключевым словом let, вместо var
var request = requests[i];
var callback = function (request, error, result) {
if (error) {
onPopulation(error);
return;
}
responses[request] = result;
var l = [];
for (var K in responses)
l.push(K);
if (l.length == 3) {
var c = [], cc = [], p = 0;
for (var i = 0; i < responses['/countries'].length; i++) {
if (responses['/countries'][i].continent === continent) {
c.push(responses['/countries'][i].name);
}
}
for (i = 0; i < responses['/cities'].length; i++) {
for (var j = 0; j < c.length; j++) {
if (responses['/cities'][i].country === c[j]) {
cc.push(responses['/cities'][i].name);
}
}
}
for (i = 0; i < responses['/populations'].length; i++) {
for (j = 0; j < cc.length; j++) {
if (responses['/populations'][i].name === cc[j]) {
p += responses['/populations'][i].count;
}
}
}
onPopulation(null, p);
}
}.bind(this, request);
getData(request, callback);
}
}
/**
* Проверка функции
* Запустить, чтобы проверить работоспособность
*/
function test() {
getContinentPopulation("Africa", function (error, population) {
if (error) {
console.error(error);
} else {
console.log('Total population in African cities:', population);
}
});
}
/**
* @param {string} city
* @param {function(Object, number)} onPopulation
*/
function getCityPopulation(city, onPopulation) {
var callback = function(error, result) {
if (error) {
onPopulation(error);
return;
}
return onPopulation(null, calcCityPopulation(city, result));
};
getData("/populations", callback);
}
/**
* @param {string} country
* @param {function(Object, number)} onPopulation
*/
function getCountryPopulation(country, onPopulation) {
var results = {
"/populations": undefined,
"/cities": undefined
};
var callback = function(request, error, result) {
if (error) {
onPopulation(error);
return;
}
results[request] = result || [];
if (results["/populations"] && results["/cities"]) {
var population = calcCountryPopulation(country, results["/cities"], results["/populations"]);
onPopulation(null, population);
}
};
for (var request in results) {
getData(request, callback.bind(null, request));
}
}
/**
* @param {string} name City or country name
* @param {function(Object, {country: number, city: number})} onPopulation
*/
function getCityOrCountryPopulation(name, onPopulation) {
// сделан отдельный метод, а не просто вызов getCountryPopulation и getCityPopulation
// что-бы не делать лишних вызовов API
var results = {
"/populations": undefined,
"/cities": undefined
};
var callback = function(request, error, result) {
if (error) {
onPopulation(error);
return;
}
results[request] = result || [];
var cities = results["/cities"];
var populations = results["/populations"];
if (populations && cities) {
// считаем и для города и для стрраны, т.к. может быть город и страна с одиним названием
var countryPopulation = calcCountryPopulation(name, cities, populations);
var cityPopulation = calcCityPopulation(name, populations);
onPopulation(null, {
country: countryPopulation,
city: cityPopulation
});
}
};
for (var request in results) {
getData(request, callback.bind(null, request));
}
}
/**
* @param {string} country
* @param {Array.<{name: string, country: string}>} cities
* @param {Array.<{name: string, count: number}>} populations
* @return {number}
*/
function calcCountryPopulation(country, cities, populations) {
var cityNamesOfCountry = cities
.filter(function(city) { return city.country === country; })
.map(function(city) { return city.name; });
var population = populations
.filter(function(city) {return cityNamesOfCountry.indexOf(city.name) !== -1;})
.reduce(function(cur, next) {return cur+next.count;}, 0);
return population;
}
/**
* @param {string} city
* @param {Array.<{name: string, count: number}>} populations
* @return {number}
*/
function calcCityPopulation(city, populations) {
for (var i in populations) {
var name = populations[i].name,
count = populations[i].count;
if (name === city) {
return count;
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment