This is an interview question. Take a list of strings that represent port ranges like these:
const systemPorts = '11000-11001, 11100-11101, 12000-12010, 13000';
const customerports = '11000-11003, 11100-11106, 12000-12015, 13000-13003';
And return a merged list of ports in the form of a string:
'11100-11106,12000-12015,13000-13003,11000-11003'
Please review with an eye on commenting and code density, I feel I may have gone too far.
//ranges is a list of objects with a min and max property
//range is an object with a min and max property that will be merged in to ranges
function mergeRange(ranges, range){
const [min,max] = range.split("-").map(s=>Number(s));
//Does any existing range contain either min or max?
const matches = ranges.filter(o=>(o.min <= min && o.max >= min) || (o.min <= max && o.max >= max) || (o.min > min && o.max < max));
//Handle the different cases
if(matches.length == 0){
//Nothing matches, just add the range
ranges.push({min,max});
}else if(matches.length == 1){
//One match, extend if needed
matches[0].min = Math.min(matches[0].min, min);
matches[0].max = Math.max(matches[0].max, max);
}else{
//More than one match
//Remove the matches that are there
for(const match of matches){
const index = ranges.indexOf(match);
ranges.splice(index, 1);
}
matches = matches.concat([min,max]);
//Add a new range which contains all the matches
ranges.push({min: Math.min(...matches.map(match=>match.min)) , max: Math.max(...matches.map(match=>match.max))});
}
}
//['11000-11001, 11100-11101, 12000-12010, 13000', '11000-11003, 11100-11106, 12000-12015, 13000-13003']
//becomes '11100-11106,12000-12015,13000-13003,11000-11003'
function mergePortRanges(portRangesList){
let ranges = [];
for(const portRanges of portRangesList){
for(let range of portRanges.split(",").sort().map(s=>s.trim())){
//make a single number a range (13000 -> 13000-13000)
range = range.includes('-') ? range : `${range}-${range}`;
mergeRange(ranges, range);
}
}
return ranges.map(range => `${range.min}-${range.max}`).join(",");
}
const systemPorts = '11000-11001, 11100-11101, 12000-12010, 13000';
const customerports = '11000-11003, 11100-11106, 12000-12015, 13000-13003';
console.log(mergePortRanges([systemPorts, customerports]));
console.log(mergePortRanges(['11000-11121, 11100-11101']));
1 Answer 1
One criticism I will give you is I think you went overboard with the comments. Some of the code will speak for itself if it's well written. Not saying my code is well written, I'm just saying.
Here's what I came up with. I'm afraid it's not much cleaner than your version, if at all. I spent a good amount of time on it too.
function mergePortRanges(input){
//Group all ranges into one array
let portList = [];
input.forEach( list => {
portList.push(...list.split(',').map( range => range.trim()));
});
//Break the ranges into objects with min max integers
portList = portList.map( range => {
range = range.split('-').map( s => +s );
if(range.length === 1)
range.push(range[0]);
return { min: range[0], max: range[1] };
});
//Sort by mins
portList.sort((a,b) => (a.min > b.min) ? 1 : -1);
//Group ranges starting with same min and grab the highest one.
let newPortList = [];
let curRangeMin = 0;
for(let x = 0; x < portList.length; x++){
let range = portList[x];
if(curRangeMin === range.min) continue;
else curRangeMin = range.min;
let group = portList.filter( old => range.min == old.min); //Get full range
group.map( (a,b) => (a.max > b.max) ? 1 : -1 ); //Sort by max
newPortList.push( group[0] ); //Grab highest in range and push.
}
return newPortList.map( range => {
if(range.min == range.max)
return range.min;
else
return `${range.min}-${range.max}`;
}).join(',');
}
I still feel like this could be refined quite a bit, but I've already spent so much time on it lol. Maybe I'll try again when I'm smarter.
-
\$\begingroup\$ Yeah... this last for loop won't work on certain inputs. I'm working to refine it. \$\endgroup\$Rager– Rager2019年11月15日 20:58:29 +00:00Commented Nov 15, 2019 at 20:58
-
\$\begingroup\$ Alright, I think it's looking alright. Based on what I did just now, I feel like you did a good job on yours. \$\endgroup\$Rager– Rager2019年11月15日 21:48:55 +00:00Commented Nov 15, 2019 at 21:48
'11000-11121, 11100-11101, ...
? If so, how it would effect the final result? \$\endgroup\$11000-11121
\$\endgroup\$'5-10, 4-12, 1-13'
should output1-13
and'5-10, 4-12, 13'
should output4-13
\$\endgroup\$