Примерно год назад собеседовался с немцами, на одном из этапов дали это легкое тестовое задание. Мое решение их устроило, но позже уже третье собеседование по алгоритмам и теории я завалил капитально.
Наткнулся на него случайно, разгребая старую почту. Оцените мое решение, кто в теме. Как часто спрашивают во всяких бизнес линчах "Стоит продолжать развиваться в этом направлении или забыть?"
Условие:
Представьте, что вы работаете в компании, которая помогает американским фирмам импортировать свои товары для продажи в ЕС. Давайте представим, что каждый заказ может быть представлен в виде кортежа(tuple по англ, надеюсь правильно перевел) - (orderId, companyName, customerAdress, orderedItem).
Пример того, как может выглядеть такой набор данных в файле:
001, SuperTrader, Steindamm 80, Macbook
002, Cheapskates, Reeperbahn 153, Macbook
003, MegaCorp, Steindamm 80, Book "Guide to Hamburg"
004, SuperTrader, Sternstrasse 125, Book "Cooking 101"
005, SuperTrader, Ottenser Hauptstrasse 24, Inline Skates
006, MegaCorp, Reeperbahn 153, Playstation
007, Cheapskates, Lagerstrasse 11, Flux compensator
008, SuperTrader, Reeperbahn 153, Inline Skates
Нужно реализовать:
1) Отобразить все заказы конкретной компании
2) Показать все заказы по конкретному адресу
3) Удалить какой-то заказ по его OrderId
4) Отобразить самые популярные товары по убыванию
Мое решение (все лишнее вроде server.js, routes.js и тд опущены, ниже только сам контроллер):
// app/controller.js
var fs = require('fs'),
byline = require('byline');
function find(position, value, next) {
var content,
temp,
data = new Array();
value = value.trim();
var stream = byline(fs.createReadStream('sample', {encoding: 'utf8'}));
stream.on('readable', function() {
while ((content = stream.read()) !== null) {
content = content.split(/\r?\n/);
content.forEach(function(item) {
if (!item) {
return;
}
temp = item.split(',');
// if there are some wrong format data
// for example delivery address with comma "Reeperbahn, 153"
// we can do nothing and just skip this broken line
if (temp.length != 4) {
return;
}
if (temp[position].trim() == value) {
data.push(temp);
}
});
}
});
stream.on('end', function() {
next(null, data); return;
});
}
function getTopSellers(req, next) {
var content,
temp,
data = {},
companies = new Array();
var stream = byline(fs.createReadStream('sample', {encoding: 'utf8'}));
stream.on('readable', function() {
while ((content = stream.read()) !== null) {
content = content.split(/\r?\n/);
content.forEach(function(item) {
// empty line?
if (!item) {
return;
}
temp = item.split(',');
if (temp.length != 4) {
return;
}
// temp[3] is a product name
if (data[temp[3]]) {
data[temp[3]] += 1;
} else {
data[temp[3]] = 1;
}
// if not ajax, then create also companies list
if (!req.xhr) {
if (companies.indexOf(temp[1]) < 0 && typeof temp[1] != undefined) {
companies.push(temp[1]);
}
}
});
}
});
stream.on('end', function() {
var top = new Array();
var temp = {};
for(var key in data) {
temp[key] = data[key];
top.push(temp);
temp = {};
}
top.sort(compare);
next(null, top, companies); return;
});
}
function deleteOrder(id, next) {
var content,
temp,
data = '',
storage,
found = false;
// we are blocking the whole process, while deleting an order
// to avoid loosing of data, if new orders appearing very quickly
content = fs.readFileSync('sample', {encoding: 'utf8'});
content = content.split(/\r?\n/);
content.forEach(function(item) {
storage = item;
temp = item.split(',');
if (temp.length != 4) {
return;
}
if (temp[0].trim() == id) {
found = true;
} else {
data += storage + '\n';
}
});
if (found) {
fs.writeFileSync('sample', data);
next(null, "ok"); return;
} else {
next(null, "error"); return;
}
}
function compare(a,b) {
for (var a_key in a) {
for (var b_key in b) {
if (a[a_key] > b[b_key])
return -1;
if (a[a_key] < b[b_key])
return 1;
return 0;
}
}
}
module.exports.getTopSellers = getTopSellers;
module.exports.find = find;
module.exports.deleteOrder = deleteOrder;
Интересует в принципе общая оценка решения, но есть
один главный вопрос. Как в таком случае нужно и вообще правильно делать удаление заказа? В моем решении я считываю по строке из файла и затем, после прохода по всему файлу, перезаписываю его полностью без той строки, в которой нужно было найти orderID и удалить. Для этого мне конечно же приходится блокировать весь процесс, чтобы параллельно не могло быть другой записи в файл из Ноды, чтобы избежать перезаписи \ потери данных. Для небольших файлов оно еще работает, но что если этот файлик с заказами будет 10GB, например? Скажем это все заказы текущего месяца, миллионы их. Стоит об этом вообще париться, или так вряд ли бывает и задача не корректна? Как лучше решить задачу для файла с 10GB данных, куда каждую минуту пишутся новые заказы, отменяются старые и тд (Амазон, например).