Sublime Forum

Sort lines numeric

#1

Am I just not seeing this?

0 Likes

#2

Do you mean alphabetically?

0 Likes

#3

No… I mean numerically.

Given:100 20 2 10 5 1

Sublime sorts to the following:1 10 100 2 20 5

I expect (or want an option for):1 2 5 10 20 100

0 Likes

#4

Coming from Notepad2, I couldn’t find this feature as well.

In Notepad2, there’s a sort dialog that implements this option with “Logical number comparison”. Here’s a screenshot: http://i.imgur.com/eb0iU.png

0 Likes

#5

Implemented this today. Code is near verbatim from this StackOverflow answer .

Modifying the default sort.py means changes will be lost on upgrade. Maybe Jon will include this in a future build. :wink:

Open /Packages/Default/sort.py . Add an import:

import re

Then look for the case_insensitive_sort and case_sensitive_sort functions, and modify them to convert to int’s when necessary:

[code]def case_insensitive_sort(txt):

txt.sort(lambda a, b: cmp(a.lower(), b.lower()))

return txt

convert = lambda text: int(text) if text.isdigit() else text.lower()
alphanum_key = lambda key:  convert(c) for c in re.split('([0-9.,]+)', key) ]
return sorted(txt, key=alphanum_key)

def case_sensitive_sort(txt):

txt.sort(lambda a, b: cmp(a, b))

return txt

convert = lambda text: int(text) if text.isdigit() else text
alphanum_key = lambda key:  convert(c) for c in re.split('([0-9.,]+)', key) ]
return sorted(txt, key=alphanum_key)[/code]

Here is the full file:

[code]import sublime, sublime_plugin
import random
import re

Uglyness needed until SelectionRegions will happily compare themselves

def srcmp(a, b):
aa = a.begin();
ba = b.begin();

if aa < ba:
    return -1;
elif aa == ba:
    return cmp(a.end(), b.end())
else:
    return 1;

def srtcmp(ta, tb):
return srcmp(ta[0], tb[0])

def permute_selection(f, v, e):
regions = [s for s in v.sel() if not s.empty()]
regions.sort(srcmp)
txt = [v.substr(s) for s in regions]
txt = f(txt)

# no sane way to handle this case
if len(txt) != len(regions):
    return

# Do the replacement in reverse order, so the character offsets don't get
# invalidated
combined = zip(regions, txt)
combined.sort(srtcmp, reverse=True)

for x in combined:
    [r, t] = x
    v.replace(e, r, t)

def case_insensitive_sort(txt):

txt.sort(lambda a, b: cmp(a.lower(), b.lower()))

return txt

convert = lambda text: int(text) if text.isdigit() else text.lower()
alphanum_key = lambda key:  convert(c) for c in re.split('([0-9.,]+)', key) ]
return sorted(txt, key=alphanum_key)

def case_sensitive_sort(txt):

txt.sort(lambda a, b: cmp(a, b))

return txt

convert = lambda text: int(text) if text.isdigit() else text
alphanum_key = lambda key:  convert(c) for c in re.split('([0-9.,]+)', key) ]
return sorted(txt, key=alphanum_key)

def reverse_list(l):
l.reverse()
return l

def shuffle_list(l):
random.shuffle(l)
return l

def uniquealise_list(l):
table = {}
res = ]
for x in l:
if x not in table:
table[x] = x
res.append(x)
return res

permute_funcs = { “reverse” : reverse_list,
“shuffle” : shuffle_list,
“unique” : uniquealise_list }

def unique_selection(v):
regions = [s for s in v.sel() if not s.empty()]
regions.sort(srcmp)

dupregions = ]
table = {}
for r in regions:
    txt = v.substr(r)
    if txt not in table:
        table[txt] = r
    else:
        dupregions.append(r)

dupregions.reverse()
for r in dupregions:
    v.erase(e, r)

def shrink_wrap_region( view, region ):
a, b = region.begin(), region.end()

for a in xrange(a, b):
    if not view.substr(a).isspace():
        break

for b in xrange(b-1, a, -1):
    if not view.substr(b).isspace():
        b += 1
        break

return sublime.Region(a, b)

def shrinkwrap_and_expand_non_empty_selections_to_entire_line(v):
sw = shrink_wrap_region
regions = ]

for sel in v.sel():
    if not sel.empty():
        regions.append(v.line(sw(v, v.line(sel))))
        v.sel().subtract(sel)

for r in regions:
    v.sel().add(r)

def permute_lines(f, v, e):
shrinkwrap_and_expand_non_empty_selections_to_entire_line(v)

regions = [s for s in v.sel() if not s.empty()]
if not regions:
    regions = [sublime.Region(0, v.size())]

regions.sort(srcmp, reverse=True)

for r in regions:
    txt = v.substr(r)
    lines = txt.splitlines()
    lines = f(lines)

    v.replace(e, r, u"\n".join(lines))

def has_multiple_non_empty_selection_region(v):
return len([s for s in v.sel() if not s.empty()]) > 1

class SortLinesCommand(sublime_plugin.TextCommand):
def run(self, edit, case_sensitive=False,
reverse=False,
remove_duplicates=False):
view = self.view

    if case_sensitive:
        permute_lines(case_sensitive_sort, view, edit)
    else:
        permute_lines(case_insensitive_sort, view, edit)

    if reverse:
        permute_lines(reverse_list, view, edit)

    if remove_duplicates:
        permute_lines(uniquealise_list, view, edit)

class SortSelectionCommand(sublime_plugin.TextCommand):
def run(self, edit, case_sensitive=False,
reverse=False,
remove_duplicates=False):

    view = self.view

    permute_selection(
        case_sensitive_sort if case_sensitive else case_insensitive_sort,
        view, edit)

    if reverse:
        permute_selection(reverse_list, view, edit)

    if remove_duplicates:
        unique_selection(view, edit)

def is_enabled(self, **kw):
    return has_multiple_non_empty_selection_region(self.view)

class PermuteLinesCommand(sublime_plugin.TextCommand):
def run(self, edit, operation=‘shuffle’):
permute_lines(permute_funcs[operation], self.view, edit)

class PermuteSelectionCommand(sublime_plugin.TextCommand):
def run(self, edit, operation=‘shuffle’):
view = self.view

    if operation == "reverse":
        permute_selection(reverse_list, view, edit)

    elif operation == "shuffle":
        permute_selection(shuffle_list, view, edit)

    elif operation == "unique":
        unique_selection(view, edit)

def is_enabled(self, **kw):
    return has_multiple_non_empty_selection_region(self.view)[/code]
0 Likes

#6

I made changes in sort.py accordingly but didn’t work for me.
Anyone, have any idea what to do ???
Please Reply soon …
Thanks

0 Likes

#7

Sorry, my mistake. I got this. it only works for numerical values.
I need to do it for css Properties
Like:
#topbar{
background-image:url(“images/abc.png”);
background-position: 12px 13px;
border-style: solid;
font-size: 1px;
left: 36px;
margin-top: 10px;
top: 23px;
vertical-align: center;
z-index: 1;
}
should convert into:
#topbar{
left: 36px;
top: 23px;
z-index: 1;
font-size: 1px;
margin-top: 10px;
border-style: solid;
vertical-align: center;
background-position: 12px 13px;
background-image:url(“images/abc.png”);
}

please, anyone can help me…

Thanks

0 Likes

#8

Here’s a method I used to sort numerically. Given the data:

100
20
2
10
5
1

Get insertion points at the start of each line (Windows: Ctrl+A, Ctrl+Shift+L, Home), then insert at least as many spaces at the start of the lines as your widest number (here it’s 100, so at least 3 spaces). Should now look like this (| are the insertion points):

   |100
   |20
   |2
   |10
   |5
   |1

Now press End:

   100|
   20|
   2|
   10|
   5|
   1|

Cursor back to the left past the digits:

   |100
  | 20
 |   2
  | 10
 |   5
 |   1

Selected the leading space and delete it (Ctrl+Shift+Left, Del). Because the insertion points were relative to the length of the numbers, all the numbers should now line up on the right (now . represents space because I couldn’t get it to render right otherwise):

100
.20
..2
.10
..5
..1

So now you can just do a normal Edit - Sort Lines, and you’ll get a numeric sorting:

..1
..2
..5
.10
.20
100

If you want decending you can then do Edit - Permute Lines - Reverse. To get rid of the leading space, Ctrl+A, Ctrl+Shift+L, Home, Home, Ctrl+Shift+Right, Ctrl+Shift+Left, Del.

1 Like

#9

Hey,

I wrote a simple plugin that adds the command “Sort Numerically” to the palette:

github.com/alimony/sublime-sort-numerically

(It’s my first ever ST plugin so I would really appreciate some testing.)

1 Like

#10

This is so cool to do!

0 Likes

#11

you’re a life-saver <3 this should be incorporated into Sublime

0 Likes