Ruby / sorbarendezés alfanumerikusan és numerikusan is

Felhasználó által megadott neveket akartam sorba rendezni úgy név szerint, hogy figyelembe vegye a számokat is (az adatok MySQL db-ből jönnek). Például legyenek a címek:


world
hello 15
hello 2

Csak alfanumerikusan rendezve így néz ki:


hello 15
hello 2
world

Én pedig ezt akartam:


hello 2
hello 15
world

Úgy oldottam meg, hogy átkonvertálok a sorba rendezéshez minden szöveget úgy, hogy minden egyes szám elé beteszek annyi nullát, hogy egy fix hosszot adjon ki a számjegyekkel együtt, majd sorba rendezem őket (ezt megelőzően a db nekem már név szerint sorba hozta).

Legyen az adat egy tömbben:

t = ["hello 15", "hello 2", "world"]

Kód:


# add preceeding zeros to every numeric part in a text
def pad2(text)
	zeros = 20
	res = ""
	a1 = text.split(/[0-9]+/)
	a2 = text.scan(/[0-9]+/)
	if a2.size > 0
		c = [a1.size, a2.size].max
		i = 0
		loop{
			x1 = a1[i]
			x2 = a2[i]
			if x1
				res << x1
			end
			if x2
				len = zeros - x2.size
				len = 0 if len < 0
				x3 = "0" * len + x2
				res << x3
			end
			i += 1
			break if i >= c
		}
	else
		res = text
	end
	return res
end


t2 = t.sort_by{|x| pad2(x)}
p t2

Eredmény:

["hello 2", "hello 15", "world"]

Lásd még a kapcsolódó előző blogbejegyzésem.

toMpEr ötletét felhasználva egy jobb metódus:

p t.sort_by{|x| x.split(/(\d+)/).map{|x| x[/\d/] ? x.to_i : x} }

Egy strip-et beletettem még, hogy ne számítson a sorbarendezésnél a szóközök (és tab-ok) mennyisége, vagyis "hello 1" és "hello     1" bemenetet vegye egyformának:

p t.sort_by{|x| x.split(/(\d+)/).map{|x| x[/\d/] ? x.to_i : x.strip} }

Hozzászólások

Python:


t = ["hello 15", "hello 2", "hello", "hello a", "world"]

def strSorter(key):
	if " " in key:
		pre, last_word = key.rsplit(" ", 1)
		if last_word.isdigit():
			return (pre, int(last_word))
		else:
			return (key, 0)
	else:
		return (key, 0)

t2 = sorted(t, key=strSorter)

assert t2 == ['hello', 'hello 2', 'hello 15', 'hello a', 'world']

Bar Ruby-t nem ismerem, de ott is biztos van lehetoseg tobb szintu sort-ra.

Nem egészen jó az általad írt kód. Nem univerzális. Ha jól olvasom akkor szóközzel osztod fel és az utolsó szót (részt) nézed hogy szám-e stb. Viszont ez nem működik bármilyen kombinációra, pl:

ez egy példa 1111 van további része is 4433 és egyéb szöveg
ez egy példa 2 itt meg még több szöveg van

Az én algo-m jól rendezi, de a tiéd pedig nem.

Amennyiben ez kovetelmeny:


import re

t = [
	"hello 15", "hello 2", "hello", "hello a", "world",
	"ez egy pelda 2 valami van meg hatra 15", "ez egy pelda 15 valami van meg hatra", "ez egy pelda 2 valami van meg hatra 2"
]

def strSorter(key):
	parts = re.split("([0-9]+)", key)
	parts = [int(part) if part.isdigit() else part for part in parts]
	return parts

t2 = sorted(t, key=strSorter)

assert t2 == [
	'ez egy pelda 2 valami van meg hatra 2', 'ez egy pelda 2 valami van meg hatra 15', 'ez egy pelda 15 valami van meg hatra',
	'hello', 'hello 2', 'hello 15', 'hello a', 'world'
]

A végéről lehagytad a .max parancsot. Lásd ezt:


["hello world", "banana", "this is some sample text"].map{|x|x.size}.max
=> 24

Tehát mondhatom hogy 24 darab nullánál biztos hogy nem fog kelleni több a rendezéshez., merthogy ennyi karakter hosszú a leghosszabb szöveg. Tehát ha csak számból áll, pl 1, akkor sem kell hosszabb, tehát meghúzhatjuk a limitet itt.

$ printf "world\nhello 15\nhello 2\n" | sort --version-sort                                                                                         
hello 2                                                                                                                                             
hello 15                                                                                                                                            
world

Nem értem, miért kell túlbonyolítani :)

Ha jól gondolom, valójában a természetes rendezést keres(t)ed.
Python:
>>> import natsort
>>> t = ["hello 15", "hello 2", "world"]
>>> natsort.natsorted(t)
['hello 2', 'hello 15', 'world']

Bizonyára van ilyen modul Rubyban is.

+1

https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/

Szerk: ami nekem meg mindig nem tiszta ezzel a sztorival kapcsolatban: az npm-nek nincs valami local repoja mint a mavennek? Minden clean buildnel a kozponti repobol huzza a fuggosegeket?

Szerk2: ahogy latom nincs neki by default: https://www.npmjs.com/package/local-npm

Komolyan, a JS wannabe-k meg egy jol mukodo okoszisztemat se tudnak rendesen lemasolni? Mondjuk igy legalabb konzisztensek a JavaScripttel, az is nelkuloz minden jozan eszt.