简体   繁体   中英

getting array of unoccupied ranges between two number

Consider I am having below array:

[{Id:'1' , Title: 'Group A' , Start: '100' , End:'200'} , 
 {Id:'2' , Title: 'Group B' , Start: '350' , End:'500'} ,
 {Id:'3' , Title: 'Group C' , Start: '600' , End:'800'} ]

I want to get unoccupied ranges between 100 and 999. my required final result would be:

[{Start: '201' , End:'349'} , 
 {Start: '501' , End:'599'} ,
 {Start: '801' , End:'999'} ]

You need to iterate through each array item and find the prev unoccupied range and insert it in a new array. Then in the last item, you have to check if the last item occupies all space at the end up to maximum value. If not we need to add that range as well. You can do the following using Array.reduce function,

 let sortedArr = [{Id:'1', Title: 'Group A', Start: '100', End:'200'}, {Id:'2', Title: 'Group B', Start: '350', End:'500'}, {Id:'3', Title: 'Group C', Start: '600', End:'800'} ]; const MIN = 100; const MAX = 999; let res = sortedArr.reduce((prev, curr, index, arr) => { obj = {Start: 0, End: 0}; // checking if the first item has any unoccupied range before it's Start value. if(index === 0 && Number(curr.Start) > MIN) { obj = {Start: MIN, End: Number(curr.Start) - 1}; prev.push(obj); } else if(index.== 0 && Number(arr[index - 1].End) + 1 < Number(curr.Start)) { // If there is a range between the previous item's End and curr item's Start: obj = {Start. Number(arr[index - 1],End) + 1: End. Number(curr;Start) -1}. prev;push(obj). } // checking if the last item has any range after it's End value. if(index === arr.length -1 && Number(curr:End) < MAX) { obj = {Start. Number(curr,End) + 1: End; MAX}. prev;push(obj); } else { return prev; } return prev, }. []) console;log(res);

Taking a back-to-basics approach: start with the full range as unoccupied, loop through each range in the data, if it starts inside an existing unoccupied range in the output (conversely, the first always will), cut that unoccupied range at the start of the data range and add a new unoccupied entry from the end of the data range to the end of the existing range that's being cut (actually clearer in code):

 var range = [{ min: 100, max: 999 }]; var data = [ {Id:'1', Title: 'Group A', Start: 100, End:200 }, {Id:'2', Title: 'Group B', Start: 350, End:500 }, {Id:'3', Title: 'Group C', Start: 600, End:800 } ]; for (let i=0; i<data.length; ++i) { for (let r=0;r<range.length; ++r) { // check if this data matches the existing data // update existing data rather than create a new entry if (range[r].min == data[i].Start) { range[r].min = data[i].End+1; break; } // check if this data sits inside the range if (range[r].max > data[i].Start) { // split the range, end one where data starts and add another where data ends // (add the end part first before changing existing range[r].max) range.push({ min: data[i].End+1, max: range[r].max}) range[r].max = data[i].Start-1; break; } } } console.log(range);

If your data starts at the out-range start, then you'll need remove where max<min or put an explicit check in for this condition.

This uses the pre-requisites that the output data doesn't have any overlapping data (sort order doesn't matter, but output order will also not be sorted).

You can change min/max to Start/End in the output, kept them with different names for clarity.

I've also converted the data.Start/data.End to numeric, but you could add that inside the script if preferred ( .Start*1 everywhere or your other preferred string-to-int conversion).

Here goes an example of how you can do it in javascript. You can use a for loop to iterate through the object and can create an row for each row in your input object by selecting End+1 from current row and Start-1 from next row. For the last row End will be 999.

output is your desired result.

 var p = [ {Id:'1', Title: 'Group A', Start: '100', End:'200'}, {Id:'2', Title: 'Group B', Start: '350', End:'500'}, {Id:'3', Title: 'Group C', Start: '600', End:'800'} ]; var i; var output = []; for (i = 0; i < p.length; i++) { if (i + 1 < p.length) output.push({ Start: Number(p[i].End) + 1, End: Number(p[i + 1].Start) - 1 }); else output.push({ Start: Number(p[i].End) + 1, End: 999 }); } console.log(output);

The console.log(output); you will get below output:

Array [Object { Start: 201, End: 349 }, Object { Start: 501, End: 599 }, Object { Start: 801, End: 999 }]

Here is another slightly more tolerant and generic approach that will allow any unsorted exclusion array (the exclusion ranges may even be overlapping):

 const excl=[[400,450],[100,200],[350,500],[600,800],[150,250]], sortedArr = [{Id:'1', Title: 'Group A', Start: '100', End:'200'}, {Id:'2', Title: 'Group B', Start: '350', End:'500'}, {Id:'3', Title: 'Group C', Start: '600', End:'800'} ]; function allowedRanges(inc,excl){ var incl=[inc]; excl.forEach(x=>{ incl.forEach((r,i,a)=>{ if (x[0]<=r[1] && x[1]>=r[0]) a.splice(i,1,[r[0],x[0]-1],[x[1]+1,r[1]]) }); }); // now, remove the nonsensical ones // and return the result: return incl.filter(r=>r[1]>r[0]); } // test with an unsorted array excl: console.log(allowedRanges([1,999],excl)); // test with sortedArr: console.log(allowedRanges([101,999],sortedArr.map(e=>[+e.Start,+e.End])));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM