This is the new home of the egghelp.org community forum.
All data has been migrated (including user logins/passwords) to a new phpBB version.


For more information, see this announcement post. Click the X in the top right-corner of this box to dismiss this message.

Sort file data

Help for those learning Tcl or writing their own scripts.
j
juanamores
Master
Posts: 317
Joined: Sun Mar 15, 2015 9:59 am

Sort file data

Post by juanamores »

I have a file that stores 2 data:
a name and a numerical value.
It stores them as follows:

Code: Select all

set fname "ranking"
	set fp [open $fname "a"]
	puts $fp "$name" "$value"
	close $fp
Content of the file "ranking":
Ana 5
Pedro 9
José 18
Luis 3
Maria 22
... more data...
I want to sort the names of the maximum value to the minimum value, to obtain this result:
Maria 22
José 18
Pedro 9
Ana 5
Luis 3
... more data...
What procedure do I have to follow so that the names are sorted with the values ​​that correspond to them?
If you do not understand my ideas is because I can not think in English, I help me with Google Translate. I only speak Spanish. Bear with me. Thanks :)
w
willyw
Revered One
Posts: 1196
Joined: Thu Jan 15, 2009 12:55 am

Re: Sort file data

Post by willyw »

Play with this:

Code: Select all


# April 15, 2017
# http://forum.egghelp.org/viewtopic.php?t=20326


bind pub - !do_sort read_and_sort

proc read_and_sort {nick uhost handle chan text} {

        ## You need to edit the following line
        set fname "scripts/added/experimenting_for_somebody/ranking.txt"
        set fp [open $fname "r"]
        set data [read -nonewline $fp]
        close $fp

        set lines [split $data "\n"]

        putserv "privmsg $chan :lines is: $lines"

        putserv "privmsg $chan :sorted lines is: [lsort -integer -decreasing -index 1 $lines]"

}
Try it in a private test channel.
It will produce this, in your channel:
<botnick> lines is: {Ana 5} {Pedro 9} {Jos� 18} {Luis 3} {Maria 22}
<botnick> sorted lines is: {Maria 22} {Jos� 18} {Pedro 9} {Ana 5} {Luis 3}
I hope that helps to get you started.

Reference:
http://www.tcl.tk/man/tcl8.6/TclCmd/lsort.htm
and
http://forum.egghelp.org/viewtopic.php?t=6885
For a fun (and popular) Trivia game, visit us at: irc.librairc.net #science-fiction . Over 300K Q & A to play in BogusTrivia !
j
juanamores
Master
Posts: 317
Joined: Sun Mar 15, 2015 9:59 am

Post by juanamores »

Thanks for the help willyw.

Now that I think about it, I realize that my code will not work properly ...
The values ​​that are stored are daily and correspond to the points obtained by each of the nicksnames.

On the same day, a nickname can get different scores ...

I think the script should be more complex and when retrieving the data, averaging between the values ​​of each nick, and then ordering them at the end of the month.
The daily content of the ranking file will look something like this:
Ana 5
Pedro 9
Jose 18
Luis 3
Maria 22
Ana 15
Maria 3
Jose 2
Pedro 10
Ana 11
Pedro 4
Luis 3
Maria 15
Maria 18
Ana 14
Maria 3
Ana 2
From these data, I want to get the average of ordered values ​​of each nick.
Pedro's average 9+10+4= 23 23/3= 7.66 Rounded value 8
Jose's average 18+2= 20 20/2= 10
Luis's average 3+3= 6 6/2= 3
Maria's average 22+3+15+18+3= 61 61/5= 12.2 Rounded value 12
Ana's average 15+11+14+2= 42/4= 10.5 Rounded value 10

I think the best way to store the points of each nick is horizontal line (Excel spreadsheet style), to facilitate calculations without repeating the nicks name.
Example:
Pedro 9 10 4
Jose 18 2
Luis 3 3
Maria 22 3 15 18 3
Ana 15 11 14 2

Final result ordered and averaged:
Pedro 8
Jose 10
Luis 3
Maria 12
Ana 10
The process must detect if the name exists in the file and if it exists put the value in the last place on the right that is not occupied by another value.

It's more complicated than I initially thought.
If you can think of any idea to achieve it, I will be very grateful.
If you do not understand my ideas is because I can not think in English, I help me with Google Translate. I only speak Spanish. Bear with me. Thanks :)
w
willyw
Revered One
Posts: 1196
Joined: Thu Jan 15, 2009 12:55 am

Post by willyw »

juanamores wrote: ...
and correspond to the points obtained by each of the nicksnames.

On the same day, a nickname can get different scores ...
I guess you're running some kind of game.
Remember - we haven't seen it, and have no idea what you're talking about.

...The process ...
"The process" ??
What process? I don't know what you're talking about... yet.
.... put the value ..."
What value?
in the last place on the right that is not occupied by another value.

It's more complicated than I initially thought.
If you can think of any idea to achieve it, I will be very grateful.

The problem now is that I don't have a clear picture of what you are doing. So I don't know what you want to do, and can't think on it, as to what might be best.
For a fun (and popular) Trivia game, visit us at: irc.librairc.net #science-fiction . Over 300K Q & A to play in BogusTrivia !
w
willyw
Revered One
Posts: 1196
Joined: Thu Jan 15, 2009 12:55 am

Post by willyw »

In the meantime, here's something to experiment with:

Code: Select all


# April 15, 2017
# http://forum.egghelp.org/viewtopic.php?t=20326 



bind pub - !do_sort read_and_sort

proc read_and_sort {nick uhost handle chan text} {

       ## reference: http://forum.egghelp.org/viewtopic.php?t=6885

        ## Edit the following line
        set fname "scripts/added/experimenting_for_somebody/ranking.txt"
        set fp [open $fname "r"]
        set data [read -nonewline $fp]
        close $fp

        set lines [split $data "\n"]

        putserv "privmsg $chan :lines is: $lines"

        ###putserv "privmsg $chan :sorted lines is: [lsort -integer -decreasing -index 1 $lines]"


        foreach el $lines {
                set the_nick [lindex $el 0]
                set scoreslist [lrange $el 1 end]

                ## reference : http://wiki.tcl.tk/819#pagetoc9326db77
                ##           : http://wiki.tcl.tk/951
                set avgscore [ expr ([join $scoreslist +])/[llength $scoreslist] ]

                set new_el "$the_nick $avgscore"
                lappend new_list $new_el
        }

        putserv "privmsg $chan : "
        putserv "privmsg $chan :new_list is: $new_list"
        putserv "privmsg $chan :sorted new_list is: [lsort -integer -decreasing -index 1 $new_list]"

}


This:
Example:
Pedro 9 10 4
Jose 18 2
Luis 3 3
Maria 22 3 15 18 3
Ana 15 11 14 2

produces this:
<botnick> lines is: {Pedro 9 10 4} {Jose 18 2} {Luis 3 3} {Maria 22 3 15 18 3} {Ana 15 11 14 2 }
<botnick>
<botnick> new_list is: {Pedro 7} {Jose 10} {Luis 3} {Maria 12} {Ana 10}
<botnick> sorted new_list is: {Maria 12} {Jose 10} {Ana 10} {Pedro 7} {Luis 3}
For a fun (and popular) Trivia game, visit us at: irc.librairc.net #science-fiction . Over 300K Q & A to play in BogusTrivia !
User avatar
caesar
Mint Rubber
Posts: 3776
Joined: Sun Oct 14, 2001 8:00 pm
Location: Mint Factory

Post by caesar »

Have a look at:

Code: Select all

# read the ranking file
set fp [open "ranking.txt" "r"]
set data [read -nonewline $fp]
close $fp

# loop through the read data
foreach line [split $data "\n"] {
	# if the file doesn't have a name and rank disregard it
	if {[scan $line {%s%d} name rank] != 2} continue
	lappend ranking($name) $rank
}

# load each value in the array and make the result
foreach name [array names ranking] {
	set total 0
	# count the number of ranks
	set count [llength $ranking($name)]
	# sum the ranks
	foreach no $ranking($name) {
		incr total $no
	}
	# make the average
	set result [expr $total / $count]
	# output the result
	putlog "name: $name | result: $result"
}
Result:
name: Ana result: 8
name: Luis result: 3
name: Jose result: 7
name: Maria result: 10
name: Pedro result: 6
Your welcome. :)
Once the game is over, the king and the pawn go back in the same box.
j
juanamores
Master
Posts: 317
Joined: Sun Mar 15, 2015 9:59 am

Post by juanamores »

The idea of ​​caesar is not exactly what I had in mind, but it works!

I have modified the format to two decimal places of the variable count, to round off the final result.

Code: Select all

# count the number of ranks format 2 decimal
   set count [format "%.2f" [llength $ranking($name)]]

Code: Select all

# output the rounded result
   putlog "name: $name | result: [expr round ($result)]"
Now, I need to sort from highest to lowest results.
Instead of this:
name: Ana result: 8
name: Luis result: 3
name: Jose result: 7
name: Maria result: 10
name: Pedro result: 6
I want to get this:
name: Maria result: 10
name: Ana result: 8
name: Jose result: 7
name: Pedro result: 6
name: Luis result: 3
The caesar's code with my small modifications:

Code: Select all

bind pub - !avgs averages
proc averages {nick uhost hand chan text} {
 # read the ranking file
	set fp [open "ranking" "r"]
	set data [read -nonewline $fp]
	close $fp

	# loop through the read data
	foreach line [split $data "\n"] {
	# if the file doesn't have a name and rank disregard it
	if {[scan $line {%s%d} name rank] != 2} continue
	lappend ranking($name) $rank
	}

	# load each value in the array and make the result
	foreach name [array names ranking] {
   set total 0
   # count the number of ranks format 2 decimal
   set count [format "%.2f" [llength $ranking($name)]]
   # sum the ranks
   foreach no $ranking($name) {
      incr total $no
   }
   # make the average
    set result [expr $total / $count]
   # output the rounded result
   putlog "name: $name | result: [expr round ($result)]"
  }
}
Now I need to sort the results, from highest to lowest. :)


willyw, I appreciate your contributions, it is not a game, I did not copy more code because I only need to sort the values ​​of a file.
caesar understood my idea, now I just need to modify the caesar's code to get the ranking ordered from highest to lowest.
If you do not understand my ideas is because I can not think in English, I help me with Google Translate. I only speak Spanish. Bear with me. Thanks :)
w
willyw
Revered One
Posts: 1196
Joined: Thu Jan 15, 2009 12:55 am

Post by willyw »

juanamores wrote: Now I need to sort the results, from highest to lowest. :)

willyw, I appreciate your contributions, it is not a game, I did not copy more code because I only need to sort the values ​​of a file.
caesar understood my idea, now I just need to modify the caesar's code to get the ranking ordered from highest to lowest.
Ok.
I showed you one way to sort it, above. You can adapt from that.

Good luck with it. :)
For a fun (and popular) Trivia game, visit us at: irc.librairc.net #science-fiction . Over 300K Q & A to play in BogusTrivia !
User avatar
caesar
Mint Rubber
Posts: 3776
Joined: Sun Oct 14, 2001 8:00 pm
Location: Mint Factory

Post by caesar »

Code: Select all

bind pub - !avgs averages

proc averages {nick uhost hand chan text} {
	set fp [open "ranking" "r"]
	set data [read -nonewline $fp]
	close $fp
	foreach line [split $data "\n"] {
		if {[scan $line {%s%d} name rank] != 2} continue
		lappend ranking($name) $rank
	}
	foreach name [array names ranking] {
		set total 0
		set count [llength $ranking($name)] 
		foreach no $ranking($name) {
			incr total $no
		}
		set average [expr round ([expr $total / $count])]
		lappend result($name) $average
	}
	set data [lsort -stride 2 -index 1 -integer -decreasing [array get result]]	
	foreach {name score} $data {
		putlog "$name $score"
	}
}
Should do what you wanted.
Maria 12
Jose 10
Ana 9
Pedro 8
Luis 3
If you want to get an rounded result like:
Maria 12.20
Jose 10.00
Ana 9.40
Pedro 7.67
Luis 3.00
then replace:

Code: Select all

set average [expr round ([expr $total / $count])]
and 
set data [lsort -stride 2 -index 1 -integer -decreasing $data]
with:

Code: Select all

set average [format "%.2f" [expr $total / $count]]
and
set data [lsort -stride 2 -index 1 -real -decreasing $data]
Once the game is over, the king and the pawn go back in the same box.
j
juanamores
Master
Posts: 317
Joined: Sun Mar 15, 2015 9:59 am

Post by juanamores »

willyw wrote:I showed you one way to sort it, above. You can adapt from that.
In your solution, you get the result by sorting a list, but with the caesar code I have to sort an array.
I haven't idea how to integrate both ideas... :oops:
If you do not understand my ideas is because I can not think in English, I help me with Google Translate. I only speak Spanish. Bear with me. Thanks :)
User avatar
caesar
Mint Rubber
Posts: 3776
Joined: Sun Oct 14, 2001 8:00 pm
Location: Mint Factory

Post by caesar »

Here's a clean and a lot shorter code:

Code: Select all

bind pub - !avgs averages

proc averages {nick uhost hand chan text} {
	set fp [open "ranking" "r"]
	set data [read -nonewline $fp]
	close $fp
	foreach line [split $data "\n"] {
		if {[scan $line {%s%d} name rank] != 2} continue
		lappend ranking($name) $rank
	}
	foreach {name values} [array get ranking] {
		set average [expr ([join $values +]) / [llength $values]]
		lappend result($name) $average
	}
	set results [lsort -stride 2 -index 1 -integer -decreasing [lsort -stride 2 -index 0 [array get result]]]
	foreach {name score} $results {
		putlog "$name $score"
	}
}
The result is the same:
Maria 12
Jose 10
Ana 9
Pedro 7
Luis 3
The data I used is from what you posted at your second comment. Isn't the result what you are looking for?

Edit: Shorted the code removing unnecessarily loops. Don't know why I didn't think about this in the first place. :roll:

Second edit: Since your initial list doesn't have two names with an identical result I didn't see:
Maria 12
Jose 10
Ana 9
Pedro 7
Luis 3
Ben 3
Notice that the list is sorted only by average (descending) and member names aren't sorted so to get them sorted as well changed the code to first sort by name as a secondary key and then sort by average (descending) as a primary key.
So we:

Code: Select all

# build a flat list
set results [array get result]
# sort by name as a secondary key
set results [lsort -stride 2 -index 0 $result]
# then sort by average (descending) as a primary key.
set results [lsort -stride 2 -index 1 -integer -decreasing $results]
and all 3 lines combined:

Code: Select all

set results [lsort -stride 2 -index 1 -integer -decreasing [lsort -stride 2 -index 0 [array get result]]]
Result:
Maria 12
Jose 10
Ana 9
Pedro 7
Ben 3
Luis 3
Is this final result what you where looking for? :)
Last edited by caesar on Mon Apr 17, 2017 1:33 pm, edited 1 time in total.
Once the game is over, the king and the pawn go back in the same box.
j
juanamores
Master
Posts: 317
Joined: Sun Mar 15, 2015 9:59 am

Post by juanamores »

caesar your code gives an error:
Tcl error [averages]: bad option "-stride": must be -ascii, -command, -decreasing, -dictionary, -increasing, -index, -indices, -integer, -nocase, -real, or -unique
If you do not understand my ideas is because I can not think in English, I help me with Google Translate. I only speak Spanish. Bear with me. Thanks :)
User avatar
caesar
Mint Rubber
Posts: 3776
Joined: Sun Oct 14, 2001 8:00 pm
Location: Mint Factory

Post by caesar »

You seem to have an old TCL library, cos stride is in version 8.6 and upwards. Since I got 8.6 I can't test if replacing:

Code: Select all

set results [lsort -stride 2 -index 1 -integer -decreasing [lsort -stride 2 -index 0 [array get result]]] 
with:

Code: Select all

set results [lsort -index 1 -integer -decreasing [lsort -index 0 [array get result]]] 
will work so let me know to find another solution.
Once the game is over, the king and the pawn go back in the same box.
w
willyw
Revered One
Posts: 1196
Joined: Thu Jan 15, 2009 12:55 am

Post by willyw »

juanamores wrote:caesar your code gives an error:
Tcl error [averages]: bad option "-stride": must be -ascii, -command, -decreasing, -dictionary, -increasing, -index, -indices, -integer, -nocase, -real, or -unique
do:
.status
in the partyline, and note the version of TCL your bot is using, and post it here for caesar .

edit:
We posted at the exact same time! :)
For a fun (and popular) Trivia game, visit us at: irc.librairc.net #science-fiction . Over 300K Q & A to play in BogusTrivia !
j
juanamores
Master
Posts: 317
Joined: Sun Mar 15, 2015 9:59 am

Post by juanamores »

running eggdrop v1.6.21
Tcl version: 8.5.13 (header version 8.5.13)
If you do not understand my ideas is because I can not think in English, I help me with Google Translate. I only speak Spanish. Bear with me. Thanks :)
Post Reply