1

I am trying to setup a function or a class to store unique integers with abilities to add,remove or check for existence. This of course can be easily achieved using normal set. The tricky part here is displaying of ranges. I mean, if I have all the numbers between 100 and 20000 I don't want to display a huge list all the numbers in between, but rather displaying 100-20000.

Consider the following example:

numbers 3000,3008-3015,3020,3022,3030-3043,3068

The goal is to create a function or class to add, check or retrieve information about current numbers. Here is how I imagine this:

>>> f_check(3016) 
False
>>> f_check(3039)
True
>>> f_add(3016)
3000,3008-3016,3020,3022,3030-3043,3068
>>> f_remove(3039)
3000,3008-3016,3020,3022,3030-3038,3040-3043,3068    
>>> f_add(3100)
3000,3008-3016,3020,3022,3030-3038,3040-3043,3068,3100

and so forth...

Again, pay attention to the range part (3030-3038, 3040-3043) etc. I don't want to display every single entry for the consecutive numbers but rather displaying a "summary" or range.

Using the same example again, if I add 3021, I'll expect the following result:

>>> f_add(3021)
3000,3008-3016,3020-3022,3030-3043,3068

Many thanks in advance for your thoughts!

Danail Petrov
  • 1,875
  • 10
  • 12
  • Please show what code you have written so far. – Annapoornima Koppad Sep 06 '16 at 19:49
  • I haven't done anything yet. I am looking for a build-in method (if there is such) that I can use. No need to re-invent the wheel :-) – Danail Petrov Sep 06 '16 at 20:02
  • 1
    Just use a list man. What your are describing in your question, A list already has, minus the checking part. – Christian Dean Sep 06 '16 at 20:03
  • Well, I can do that, for sure, but there should be logic behind that list in order to order the numbers and ranges... and this is the part I am looking for. – Danail Petrov Sep 06 '16 at 20:05
  • Well you should add that to your answer then. That is what you're really asking ;) – Christian Dean Sep 06 '16 at 20:06
  • 3
    before we all jump down his throat without reading the full question, his addition of ranges does make this a bit less trivial... – Aaron Sep 06 '16 at 20:17
  • @DanailPetrov what type of variable is your input? a single string? a list of strings? a list of strings and numbers? – Aaron Sep 06 '16 at 20:20
  • 1. No, there's not a built-in datatype for that. 2. I would write my own custom class. People here unlikely to help further until you show some of your own efforts. – wim Sep 06 '16 at 20:21
  • Thanks @Aaron for your effort! The goal is to check/add/remove only one number at a time. – Danail Petrov Sep 06 '16 at 20:23
  • @wim - I am going to do so, but first I wanted to check if there is something build-in already. Like I said - no need to re-invent the wheel. Thanks anyway! – Danail Petrov Sep 06 '16 at 20:24
  • Much improved. Please post your solution as an answer below. After a day or so you can accept your own answer. – Dour High Arch Sep 10 '16 at 18:12

3 Answers3

1

As people in the comments say, most of your operations are simple list operation. However, if your data contains ranges (e.g. 3008-3015), it then becomes a different story. The reason is that you need some sort of decoding to what that range means. I wrote a simple code using only functions (no classes) that will do just that.

matched = False

Check whether a number or a range:

def valueCheck(val):
    if "-" in val:
        return False
    else:
        return True

Encode the range into two values (start, end)

def rangeCheck(val):
    if valueCheck(val):
        return val, val
    else:
        return val.split("-")

Check whether a number matches the content of a list:

def f_check(new_number, numbers_list):
    global matched
    for i in numbers_list:
        rangeCheck(i)
        if int(rangeCheck(i)[0]) <= int(new_number) <= int(rangeCheck(i)[1]):
            print "{} matches {}".format(new_number, i)
            matched = True
            break
    if not matched:
        print "{} doesn't exist in your list".format(new_number)

Add a number to the list:

def f_add(new_number, numbers_list):
    numbers_list.append(new_number)
    print numbers_list

Delete a number from the list:

def f_delete(new_number, numbers_list):
    numbers_list.remove(new_number)
    print numbers_list

Assuming your numbers are string values in a list like so:

numbers = ["3000", "3008-3015", "3020", "3022", "3030-3043", "3068"]

No match:

f_check("2000", numbers)
2000 doesn't exist in your list

Single match:

f_check("3020", numbers)
3020 matches 3020

Range match:

f_check("3010", numbers)
3010 matches 3008-3015

Other simple operations: Add:

f_add("2000", numbers)
['3000', '3008-3015', '3020', '3022', '3030-3043', '3068', '2000']

Delete:

f_delete("3068", numbers)
['3000', '3008-3015', '3020', '3022', '3030-3043']

Please note that single match can also be done much easier by using the following:

number = "3020"
if number in numbers:
    print "{} matched".format(number)
3020 matched

UPDATE #1


To overcome your raised "problem" of adding number to existing ranges and/or making new range if needed. I found a similar question here How to group list of continuous values in ranges, which can solve part of your issue. However, your original encoding (val-val) isn't going to be helpful in this case. To solve this, you can do the following:

Step 1, comment these two lines:

# rangeCheck(i)
# if int(rangeCheck(i)[0]) <= int(new_number) <= int(rangeCheck(i)[1]):
"""
you will not be using the rangeCheck() or valueCheck() anymore.
"""

Step 2, add this line instead of the original IF-statement:

if int(i[0]) <= int(new_number) <= int(i[1]):

Step 3, add this function which will flatten your list of numbers

def flatList(numbers_list):
    for i in numbers_list:
        if len(str(i).split("-")) > 1:
            numbers_list.extend(range(int(i.split("-")[0]), int(i.split("-")[1]) + 1))
            numbers_list.remove(i)
    return numbers_list

Step 4, add this function (taken from 1) which will group your flat list into ranges of values

from operator import itemgetter
from itertools import groupby
def numbers_group(flatt_list):
    flatt_list = [int(i) for i in flatt_list]
    ranges = []
    for k, g in groupby(enumerate(flatt_list), lambda (i, x): i - x):
        group = map(itemgetter(1), g)
        ranges.append((group[0], group[-1]))
    return ranges

Usage:

numbers = ["3000", "3008-3015", "3020", "3022", "3030-3043", "3068"]

print flatList(numbers)
['3000', '3020', '3022', '3068', 3008, 3009, 3010, 3011, 3012, 3013, 3014, 3015, 3030, 3031, 3032, 3033, 3034, 3035, 3036, 3037, 3038, 3039, 3040, 3041, 3042, 3043]

print numbers_group(sorted(flatList(numbers)))
[(3008, 3015), (3030, 3043), (3000, 3000), (3020, 3020), (3022, 3022), (3068, 3068)]

numbers = f_add("3021", numbers)
print numbers_group(sorted(flatList(numbers)))
[(3008, 3015), (3030, 3043), (3000, 3000), (3020, 3022), (3068, 3068)]

numbers = f_delete("3021", numbers)
print numbers_group(sorted(flatList(numbers)))
[(3008, 3015), (3030, 3043), (3000, 3000), (3020, 3020), (3022, 3022), (3068, 3068)]
Community
  • 1
  • 1
  • Thanks Mohammed, but this doesn't quite do the job. The add operation doesn't seem to add the new number & use it within a range. >>> f_add(3021,numbers) ['3000', '3008-3015', '3020', '3022', '3028', '3030-3043', '3068', 3021] I need this to result in that: ['3000', '3008-3015', '3020-3022', '3028', '3030-3043', '3068'] – Danail Petrov Sep 06 '16 at 21:43
  • The tricky bit here is returning the sorted range values along with standalone numbers which do not belong to any sequence ... – Danail Petrov Sep 06 '16 at 21:52
  • @DanailPetrov Check my edit (update 1) to the original solution, which i believe solves your issue. – Mohammed Elmahgiubi Sep 06 '16 at 23:38
1

Okay, first of all - thanks everyone for your efforts. Much appreciated.

Here is the solution I ended up with. I have simply used a class to extend a set & modifying the output of it.

class vlans(set):
  def check(self,number):
    if number in self:
        return True
  def __str__(self):
    last = 0
    out = []
    for x in list(self):
        if len(out)>0 and last+1==int(x):
            out[-1] = out[-1].split("-")[0]+"-"+str(x)
        else:
            out.append(str(x))
        last = int(x)
  return ",".join(out)

And here is how it works:

>>> f=vlans([3008, 3009, 3010, 3011, 3012, 3013, 3014, 
    3015, 3020, 3022, 3030, 3031, 3032, 3033, 3034, 3035, 
    3036, 3037, 3038, 3039, 3040, 3041, 3042, 3000, 3068])
>>> 
>>> 
>>> print f
3000,3008-3015,3020,3022,3030-3042,3068
>>> f.check(3021)
>>> f.add(3021)
>>> f.check(3021)
True
>>> print f
3000,3008-3015,3020-3022,3030-3042,3068
>>> f.remove(3035)
>>> print f
3000,3008-3015,3020-3022,3030-3034,3036-3042,3068

Apologies for the vague description of what I needed, but hope it all makes sense now!

Danail Petrov
  • 1,875
  • 10
  • 12
0

Since you did not specify what you mean by formatting, I'm assuming you mean sorting a list of numbers based upon their value in ascending order. Also, I modified your list and removed the dashes between certain numbers.

With that being said, what I believe you're asking for is the sorted() function. Straight from the documentation:

Python lists have a built-in list.sort() method that modifies the list in-place. There is also a sorted() built-in function that builds a new sorted list from an iterable.

I'd just make a class however, and add the methods that you outlined above. The class would be pretty simple. Here's an example of what I'd do:

class IntList:
  def __init__(self, *args):
    self.lst = [int(i) for i in args]
    self.lst.sort()

  def add(self, arg):
    self.lst.append(arg)
    self.lst.sort()

  def remove(self, arg):
    self.lst.remove(arg)
    self.lst.sort()

  def check(self, arg):
    return arg in self.lst

Here is an example usage:

>>> lst = IntList(1, 2, 3, 4, 5)
>>> lst.add(6)
>>> lst.lst
[1, 2, 3, 4, 5, 6]
>>> lst.remove(2)
>>> lst.lst
[1, 3, 4, 5, 6]
>>> lst.check(4)
True
>>> lst.check(90)
False

If my assumptions are wrong please specify what exactly you mean.

Christian Dean
  • 22,138
  • 7
  • 54
  • 87