Remove overlapping ranges from a list of ranges Groovy - groovy

I need to write a code where I have a list of Ranges in Groovy. And I need to created a fresh list from that where all the ranges dont overlap.
For example if the input is: [13..15 , 14..16]
I should be able to create a list which has either [13..16] or [13..14, 14..16]
I would really appreciate any help. I have written the following code for now but its not working one bit:
def removeOverlapInRanges(ranges)
{
def cleanedRanges = []
def overLapFound = false
def rangeIsClean = true
def test = "ranges"
ranges.each
{
range->
def index = ranges.indexOf(range)
while (index < ranges.size() -1)
{
if (ranges.get(index + 1).disjoint(range) == false)
{
overLapFound = true
rangeIsClean = false
def nextRange = ranges.get(index + 1)
if (range.from > nextRange.from && range.to < nextRange.to)
cleanedRanges.add(range.from..range.to)
else if (range.from < nextRange.from && range.to < nextRange.to)
cleanedRanges.add(range.from..nextRange.to)
else if (range.from > nextRange.from && range.to > nextRange.to)
cleanedRanges.add(nextRange.from..range.to)
}
index = index + 1
}
if (rangeIsClean)
cleanedRanges.add(range)
rangeIsClean = true
test = test + cleanedRanges
}
cleanedRanges.add(0, cleanedRanges.get(cleanedRanges.size()-1))
cleanedRanges.remove(cleanedRanges.size() - 1)
if (overLapFound)
return removeOverlapInRanges(cleanedRanges)
else
return cleanedRanges
}
I passed [12..13, 17..19, 18..22,17..19, 22..23,19..20 ]
And in return I got [12..13]
Thanks in advance for any input!!

I got this:
List<Range> simplify( List<Range> ranges ) {
ranges.drop( 1 ).inject( ranges.take( 1 ) ) { r, curr ->
// Find an overlapping range
def ov = r.find { curr.from <= it.to && curr.to >= it.from }
if( ov ) {
ov.from = [ curr.from, ov.from ].min()
ov.to = [ curr.to, ov.to ].max()
simplify( r )
}
else {
r << curr
}
}
}
def ranges = [ 12..13, 17..19, 18..22, 17..19, 22..23, 19..20 ]
assert simplify( ranges ) == [ 12..13, 17..23 ]
ranges = [ -2..3, -5..-2 ]
assert simplify( ranges ) == [ -5..3 ]
ranges = [ 3..1, 1..5 ]
assert simplify( ranges ) == [ 5..1 ] // reversed as first range is reversed
ranges = [ 1..5, 3..1 ]
assert simplify( ranges ) == [ 1..5 ]
ranges = [ 1..5, 3..1, -1..-4 ]
assert simplify( ranges ) == [ 1..5, -1..-4 ]
ranges = [ 1..5, -6..-4, 3..1, -1..-4 ]
assert simplify( ranges ) == [ 1..5, -6..-1 ]
ranges = [ 1..3, 5..6, 3..5 ]
assert simplify( ranges ) == [ 1..6 ]
Though there are probably edge cases... So I'll do a bit more testing...

The following with create a simple list of your unique numbers:
def ranges = [12..13, 17..19, 18..22,17..19, 22..23,19..20 ];
def range = ranges.flatten().unique().sort()
Here is a slightly different approach that yields some nice helper methods:
def parseToRangeString(range)
{
String result = "";
range.eachWithIndex{cur,i->
def nex = range[i+1]
def start = !result || result.endsWith(",")
def cont = cur == nex?.minus(1)
if (start && cont) //starting a new section and the next item continues this seq (starting a range = 1,10)
result += "$cur-"
else if (!cont && nex) //not continuing the seq and there are more nums to process (end with more = 6,8)
result += "$cur,"
else if (!cont && !nex) //not continuing the seq but there are no more nums to process (very end = 11)
result += cur
}
return result
}
def toRange(rangeStr)
{
def ranges = rangeStr.split(",").collect{
def range = it.split("-");
new IntRange(range[0] as int, range[-1] as int)
}
}
List.metaClass.toRangeString = {
parseToRangeString(delegate)
}
List.metaClass.toRange = {
def rangeStr = parseToRangeString(delegate)
toRange(rangeStr)
}
def ranges = [12..13, 17..19, 18..22,17..19, 22..23,19..20 ];
def list = ranges.flatten().unique().sort()
assert "12-13,17-23" == list.toRangeString()
assert [12..13,17..23] == list.toRange();

Related

How is leetcode 79 false for `"bbbaabbbbbab"`?

LeetCode Problem description here.
Given an m x n grid of characters board and a string word, return true if word exists in the grid.
The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than once.
Input:
[["a","a","b","a","a","b"],
["a","a","b","b","b","a"],
["a","a","a","a","b","a"],
["b","a","b","b","a","b"],
["a","b","b","a","b","a"],
["b","a","a","a","a","b"]]
word to find :"bbbaabbbbbab"
Output:
true
Expected:
false
class Solution {
public boolean exist(char[][] board, String word) {
if (word.equals(null)) {
return false;
}
Stack<String> path = new Stack<String>();
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
boolean foundWord = dfs(board,i, j, 0, word,path);
if (foundWord == true) {
return true;
}
}
}
return false;
}
private boolean dfs(char [][] board,int i, int j, int wordIndex, String word, Stack<String> path) {
if (!path.contains("(" + String.valueOf(i) + "," + String.valueOf(j) + ")"))
{
path.push("(" + String.valueOf(i) + "," + String.valueOf(j) + ")");
}
if (wordIndex == word.length()) {
return true;
}
if (i < 0 || j < 0 || i >= board.length || j >= board[i].length){
path.pop();
return false;
}
else if (board[i][j] != word.charAt(wordIndex)) {
path.pop();
return false;
}
char oldLetter = board[i][j];
board[i][j]='*';
boolean foundWord = dfs(board,i, j - 1, wordIndex + 1, word,path) || dfs(board,i, j + 1, wordIndex + 1, word,path)
|| dfs(board,i - 1, j, wordIndex + 1, word,path) || dfs(board,i + 1, j, wordIndex + 1, word,path)
|| dfs(board,i + 1, j + 1, wordIndex + 1, word,path);
board[i][j]=oldLetter;
return foundWord;
}
}
You must search for paths with horizontal and vertical connections only, no diagonal ones. But this ...
|| dfs(board,i + 1, j + 1, wordIndex + 1, word,path);
... allows for paths containing (certain) diagonal links to be accepted.
In line 37 of your code, you have an extra DFS call dfs(board, i + 1, j + 1, wordIndex + 1, word, path) where you are going diagonally up-right direction. If you remove this then the code logic will be fine.
And I suppose you might be using the paths variable just to debug, the code's complexity is not good right now path.contains() method takes linear time to check if the element is present or not. Due to this statement, the complexity of this approach is high and it will TLE if you try to submit the code without removing it.
class Solution {
public boolean exist(char[][] board, String word) {
if (word.equals(null)) {
return false;
}
for (int row = 0; row < board.length; row++) {
for (int col = 0; col < board[row].length; col++) {
boolean foundWord = dfs(board, row, col, 0, word);
if (foundWord == true) {
return true;
}
}
}
return false;
}
private boolean dfs(char [][] board, int row, int col, int wordIndex, String word) {
if (wordIndex == word.length()) {
return true;
}
if (row < 0 || col < 0 || row >= board.length || col >= board[row].length ||
board[row][col] != word.charAt(wordIndex)
) {
return false;
}
char oldLetter = board[row][col];
board[row][col] = '*';
boolean foundWord = dfs(board, row, col - 1, wordIndex + 1, word) ||
dfs(board, row, col + 1, wordIndex + 1, word) ||
dfs(board, row - 1, col, wordIndex + 1, word) ||
dfs(board, row + 1, col, wordIndex + 1, word);
board[row][col] = oldLetter;
return foundWord;
}
}

Transform while loop into closure in Groovy

How to transform the following while code in groovy into groovy closures. Is it possible ?
List<String> list = ["s1", "s2", "s3:", "s4", "s5"]
List<String> res = []
int index = 0
while (index < res.size()) {
String cur = list[index]
if (cur.endsWith(":")) {
String next = list[index + 1] /*out of bounds won't happen*/
res.add(cur + "\n" + next)
index = index + 2
} else {
res.add(cur)
index++
}
}
assert res == ["s1", "s2", "s3:s4", "s5"]
I thought of the following solution, but seems like its not working:
List<String> list = ["s1", "s2", "s3:", "s4", "s5"]
List<String> res = list.eachWithIndex { int index, String cur ->
if (cur.endsWith(":")) {
String next = list[index + 1] /*Out of bounds won't happen*/
index = index + 2
return cur + "\n" + next
}
return cur
} as List<String>
assert res == ["s1", "s2", "s3:s4", "s5"]
I get this remark in my above code that "index = index + 2 is never used"
Consider the inject method (though this does mutate elements of the list, which seems dicey):
def list = ["s1", "s2", "s3:", "s4", "s5"]
def res = list.inject([], { acc, item ->
def lastItem = acc.isEmpty() ? null : acc.last()
if (lastItem?.endsWith(":")) {
acc[-1] += item
} else {
acc << item
}
acc
})
assert res == ["s1", "s2", "s3:s4", "s5"]
Here, acc is the "accumulated result list" and item is the current item in the original list. From here: the inject method is also known as reduce or fold in other languages.
Another answer similar to Michael Easter's with inject:
def list = ["s1", "s2", "s3:", "s4", "s5"]
def res = list.inject([]) { acc, item ->
if (acc && acc[-1].endsWith(':'))
item = acc.removeLast() + item
acc + item
}
assert res == ["s1", "s2", "s3:s4", "s5"]
with perhaps a few less characters.
Here is my second solution. It doesn't use closures but is arguably "functional". Note that it is not a great idea for large lists:
def list = ["s1", "s2", "s3:", "s4", "s5"]
def str = list.join(",")
// e.g. s1,s2,s3:,s4,s5
str = str.replaceAll(/\:,/, ":")
// e.g. s1,s2,s3:s4,s5
def res = str.split(",")
assert res == ["s1", "s2", "s3:s4", "s5"]
Not sure if this is the grooviest of all existing solution, but it works for your example:
list = ["s1", "s2", "s3:", "s4", "s5:"]
res = []
list.eachWithIndex
{e, i -> if (i > 0 && list[i-1].endsWith(":")) {return}
if (e.endsWith(":") && i < list.size-1) {res.add("${e}${list[i+1]}") } else {res.add(e)}}
which returns [s1, s2, s3:s4, s5:] as expected
You process each element in the list and
if the preceding element exists and is suffixed: do nothing
if the current element is suffixed and is not last: concatenate the current element with the next.

Why those two pieces of code return different outputs?

Code snippet #1:
d = census_df[census_df.SUMLEV==50].copy()
d['max'] = d[['POPESTIMATE2010','POPESTIMATE2011','POPESTIMATE2012','POPESTIMATE2013','POPESTIMATE2014','POPESTIMATE2015']].max(axis=1)
d['min'] = d[['POPESTIMATE2010','POPESTIMATE2011','POPESTIMATE2012','POPESTIMATE2013','POPESTIMATE2014','POPESTIMATE2015']].min(axis=1)
d['diff'] = d['max'] - d['min']
d[d['diff'] == d['diff'].max()].iloc[0]['CTYNAME']
Code snippet #2:
d = census_df[census_df["SUMLEV"] == 50]
d= d.groupby(["STNAME"])["POPESTIMATE2015'].nlargest(3)
d["max"] = d[['POPESTIMATE2010','POPESTIMATE2011','POPESTIMATE2012','POPESTIMATE2013','POPESTIMATE2014','POPESTIMATE2015']].max(axis=1)
d["min"] = d[['POPESTIMATE2010','POPESTIMATE2011','POPESTIMATE2012','POPESTIMATE2013','POPESTIMATE2014','POPESTIMATE2015']].min(axis=1)
d["Diff"] = d["max"] - d["min"]
ans = d[d["Diff"] == d["Diff"]].max().iloc[0]["CTYNAME"]

Switch values in two multi-nested maps in groovy

I am writing a method in order to replace values in one of two maps depending on if the keys match together. For example lets say we have two maps:
def oldmap = [emails: 1, files:[permissions: 3, config:4]]
def replacementmap = [emails: 2, permissions: 5]
// I want this old map to have updated values for keys that match after the method is called.
replacementPermissions(oldmap, replacementmap)
print oldmap
//prints [emails: 2, files:[permissions: 5, config:4]]
I have written this method shown below that works for one layered nested map, but I noticed a recursive solution would be a better option instead because my solution wouldn't work for multi-layered nested maps.
def replacePermissions(read, params){
read.each{x,y ->
temp = x
if (y instanceof Map){
y.each{x2,y2->
temp = x2
params.each{xx,yy->
if (temp == xx) y.put(x2, yy)
if (yy instanceof Map){
yy.each{aa, bb->
if (temp == aa) y.put(x2, bb)
}
}
}
}
}
else{
params.each{x1,y1->
if (temp == x1) read.put(x, y1)
}
}
}
return read
}
I am having trouble wrapping my head around how to think of a recursive solution for traversing and matching keys to swap values.
Right now I have this with
No signature of method: main.swapsearch() is applicable for argument types: (java.util.LinkedHashMap, java.lang.Integer) values: [[lol3:[lol5:4, lol6:10], lol4:4], 5]
Possible solutions: swapsearch(java.util.Map, java.util.Map)
def swapsearch(Map mapa, Map mapb){
mapa.each{x,y ->
temp = x
mapb.each{x1, y1->
if (y instanceof Map || y1 instanceof Map){
swapsearch(y, y1)
}
else if (temp == x1){
mapb.put(x, y1)
}
}
}
mapb
}
Map oldmap = [lol1: 1, lol2:[lol3: [lol5: 4, lol6: 10], lol4:4]]
Map newmap = [lol1: 5, lol5: 111]
print newmap
newmap = swapsearch(oldmap, newmap)
print newmap
SOLUTION with help of #injecteer:
I was able to do a simple recursion as so:
// Make sure repl map is flattened
def switchMaps( Map src, Map repl ){
src.each{key,value ->
if( repl.containsKey(key) ){
src.put(key, repl[key])
}
else if( value && value instanceof Map ){
replacemaps (value, repl)
}
}
src
}
Some simple recursion:
Map oldmap = [emails: 1, files:[permissions: 3, config:4, deep:[ deeper:[ verydeep:1 ] ] ] ]
Map replacementmap = [emails: 2, permissions: 5, verydeep:400]
def replace( Map src, Map repl ){
src.each{
if( repl.containsKey( it.key ) )
it.value = repl[ it.key ]
else if( it.value && Map.isAssignableFrom( it.value.getClass() ) )
replace it.value, repl
}
}
replace oldmap, replacementmap
assert oldmap.emails == replacementmap.emails
assert oldmap.files.permissions == replacementmap.permissions
assert oldmap.files.deep.deeper.verydeep == replacementmap.verydeep
If your replacementmap is also nested, you have to pre-process it before using, like so:
Map replacementmap = [emails: 2, permissions: 5, deep:[ config:300, toodeep:[ verydeep:400 ] ] ]
Map flatten( Map m, Map res = [:] ) {
m.each{ k, v ->
if( !v ) return
if( Map.isAssignableFrom( v.getClass() ) ) flatten v, res
else res[ k ] = v
}
res
}
Map flatRepl = flatten replacementmap
assert flatRepl == [emails:2, permissions:5, config:300, verydeep:400]

Reading CSV values and adding them to a dictionary, is there any better approach without the loop?

String[] S1 = miscParams.Split(";".ToCharArray(),
StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < S1.Count(); )
{
miscparams.Add(S1[i], S1[i + 1]);
i += 2;
}
Please don't really do this in real program:
var miscparams = S1.Where((k, i) => i%2 == 0)
.Zip(S1.Where((v, i) => i%2 == 1), (k, v) => new[] {k, v})
.ToDictionary(e => e[0], e => e[1]);

Resources