I am new to tcl. I need to sort a list of values and save indices.
I have 2 lists and I want to sort listA, but then I want to order listB preserving the indices form listA.
For example:
set listA {5 6 7 3 4 7 8 9}
set listB {0 1 2 3 4 5 6 7}
set sort_listA [lsort $listA]
Now sort_listA will be 3 4 5 6 7 7 8 9.
I want to sort listB list keeping the same indices of sort_listA like:
3 4 0 1 2 5 6 7
In other words, I need to sort both lists with the sorting of listA.
Can anyone help me?
This is exactly the sort of task for which lsort has the -indices option (requires 8.5 or later). Instead of the list of sorted values, it returns the list of indices into the original list in the order that that you would retrieve them to get a sorted list, and that's perfect for your task. This interactive session (in Tcl 8.6 so I have lmap) is indicative:
% set listA {5 6 7 3 4 7 8 9}
5 6 7 3 4 7 8 9
% set listB {0 1 2 3 4 5 6 7}
0 1 2 3 4 5 6 7
% set idxs [lsort -indices $listA]
3 4 0 1 2 5 6 7
% lmap i $idxs {lindex $listA $i}
3 4 5 6 7 7 8 9
% lmap i $idxs {lindex $listB $i}
3 4 0 1 2 5 6 7
If you're still on 8.5, you can use foreach to do the remapping; it's easier with a procedure:
proc mapindices {indexlist valuelist} {
set result {}
foreach i $indexlist {
lappend result [lindex $valuelist $i]
}
return $result
}
set listA {5 6 7 3 4 7 8 9}
set listB {0 1 2 3 4 5 6 7}
puts [mapindices [lsort -indices $listA] $listB]
Now, in 8.4 (NO LONGER SUPPORTED!) there's no indices option so you need to do more work.
proc lsortIndices {list} {
# Pair each value up with its index
set zipped {}
set idx -1
foreach val $list {
lappend zipped [list [incr idx] $val]
}
# Do the sorting by the value of the pair
set sorted [lsort -index 1 $zipped]
# Unpack the indices from the pairs to get the result
set result {}
foreach pair $sorted {
lappend result [lindex $pair 0]
}
return $result
}
However, at that point you'd probably just zip the two lists together and work more directly:
set zipped {}
foreach valA $listA valB $listB {
lappend zipped [list $valA $valB]
}
set sorted [lsort -index 0 $zipped]
set listAsorted {}
set listBsorted {}
foreach pair $sorted {
lappend listAsorted [lindex $pair 0]
lappend listBsorted [lindex $pair 1]
}
Working with older versions of Tcl than 8.4 would require you to use the -command option and that's really slow.
Related
I know that Clojure switches from PersistentArrayMap to PersistentHashMap when a map is greater than 8 in size (8 key-value pairs). This can be verified by:
(type {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8})
=> clojure.lang.PersistentHashMap
But if I create the above map using zipmap:
(type (zipmap (range 9) (range 9)))
=> clojure.lang.PersistentArrayMap
I made sure that the construction is correct:
(= (zipmap (range 9) (range 9))
{0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8})
=> true
If I use (range 10), it starts using PersistentHashMap. So for zipmap, the size has to be 9 or greater before it starts using PersistentHashMap.
I found this very weird. Why is there a difference in datastructure depending on whether I create a map manually or with zipmap?
I'm using Clojure 1.8.
This is because zipmap creates the map incrementally using assoc. Because of a bug, assoc switches from ArrayMap to HashMap for size > 9, not for 9.
Look at this:
(type {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7})
=> clojure.lang.PersistentArrayMap # size = 8
(type (assoc {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7}
8 8))
=> clojure.lang.PersistentArrayMap # size = 9
(type (assoc {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7}
8 8 9 9))
=> clojure.lang.PersistentHashMap # size = 10
The bug was fixed here: https://github.com/clojure/clojure/commit/ce9fbf61f113fdef1cbf8c1c0b4ea1a48831beef
Given the following matrix:
1 2 3 4
1 2 3 0
1 2 0 0
1 0 0 0
1 0 0 5
Return the rows that contain searched information.
For Example:
matrixname filter {{1} {} {3} {}}
The return would be:
1 2 3 4
1 2 3 0
and
matrixname filter {{1} {} {} {4}}
The return would be:
1 2 3 4
Does something like this already exist? I am thinking almost SQL-esk type of function WHERE COL = VALUE AND ORDER BY type of thing.
I have looked around and I am not finding anything.
---------------------------------EDIT
I have come up with the following to search the given fields.
::struct::list dbJoin -inner\
-keys FoundKeyList\
1 [::struct::list dbJoin -inner\
1 [MatrixName search -nocase column 2 $ITEM1]\
1 [MatrixName search -nocase column 1 $ITEM2]]\
1 [MatrixName search -nocase column 0 $ITEM3];
This will provide a list of row numbers that match the search criteria.
then you can just use MatrixName get row row or matrixName get rect column_tl row_tl column_br row_br to get the results.
Anyone have any feedback on this?
Two options come to mind.
In tcllib, there is the struct::matrix package. It has a search command. However, that command searches for patterns on individual cells (which can be constrained to particular columns) and you would need to write a procedure to perform the multiple searches required to achieve the conjunctive match you describe.
The other option is TclRAL. This will give you a relation value (aka a table) and you can perform a restrict command to obtain the subset matching an arbitrary expression, e.g.
set m [ral::relation table {C1 int C2 int C3 int C4 int}\
{1 2 3 4} {1 2 3 0} {1 2 0 0} {1 0 0 0} {1 0 0 5}]
set filt [ral::relation restrictwith $m {$C1 == 1 && $C3 == 3}]
However, both of these options are somewhat "heavyweight" and might be justified if there are more operations you need to perform on your tabular data than you indicate in your question. If the scope of your problem is as small as your questions indicates, then simply dashing off a procedure, as the other commenters have suggested, may be your best bet.
package require struct::matrix
struct::matrix xdata
xdata add columns 4
xdata add rows 5
xdata set rect 0 0 {
{1 2 3 4}
{1 2 3 0}
{1 2 0 0}
{1 0 0 0}
{1 0 0 5}
}
foreach {c r} [join [xdata search -regexp all {1*3}]] { puts [xdata get row $r] }
# 1 2 3 4
# 1 2 3 0
foreach {c r} [join [xdata search -regexp all {1*4}]] { puts [xdata get row $r] }
# 1 2 3 4
xdata destroy
This should achieve the expected results.
I am trying to recieve all values from a variable (b) when using a criterium based on another variable (a) (it's like the =IF function in excel). like this:
Example:
(a): 1 2 2 2 3 3 3 3
(b): 3 6 3 5 6 4 5 4
my criteria is
(a) = 2
my reply has to be:
(b) = 6 3 5
I tried to find a solution using arrayfun, like this:
arrayfun(#(x) b(find(a == x, 1, 'first')), 2)
obviously, it only answers the 6, the first number that matches the criterium. Can I somehow formulate arrayfun correctly? Or do I need a whole other function?
Thanks!
Don't you just want:
a = [ 1 2 2 2 3 3 3 3]
b = [3 6 3 5 6 4 5 4]
b(a == 2)
ans =
6 3 5
If a was a matrix then:
a = [ 1 2 2 2 3 3 3 3; ...
1 1 1 2 2 3 4 4; ]
b = [3 6 3 5 6 4 5 4]
b(a(1,:)==2)
ans =
6 3 5
I have a problem with removing the rows when the columns are identical.
I have used a for and if loop but the run time is too long.
I was thinking if there are any more efficient and faster run time method.
say
A=[ 2 4 6 8;
3 9 7 9;
4 8 7 6;
8 5 4 6;
2 10 11 2]
I would want the result to be
A=[ 2 4 6 8;
4 8 7 6;
8 5 4 6]
eliminating the 2nd row because of the repeated '9' and remove the 5th row because of repeated '2'.
You can use sort and diff to identify the rows with repeated values
A = A(all(diff(sort(A'))),:)
returns
A =
2 4 6 8
4 8 7 6
8 5 4 6
The trick here is how to find the rows with repeated values in an efficient manner.
How about this:
% compare all-vs-all for each row using `bsxfun`
>> c = bsxfun( #eq, A, permute( A, [1 3 2] ) );
>> c = sum( c, 3 ); % count the number of matches each element has in the row
>> c = any( c > 1, 2 ); % indicates rows with repeated values - an element with more than one match
>> A = A( ~c, : )
A =
2 4 6 8
4 8 7 6
8 5 4 6
I have array say "a"
a =
1 4 5
6 7 2
if i use function
b=sort(a)
gives ans
b =
1 4 2
6 7 5
but i want ans like
b =
5 1 4
2 6 7
mean 2nd row should be sorted but elements of ist row should remain unchanged and should be correspondent to row 2nd.
sortrows(a',2)'
Pulling this apart:
a = 1 4 5
6 7 2
a' = 1 6
4 7
5 2
sortrows(a',2) = 5 2
1 6
4 7
sortrows(a',2)' = 5 1 4
2 6 7
The key here is sortrows sorts by a specified row, all the others follow its order.
You can use the SORT function on just the second row, then use the index output to sort the whole array:
[junk,sortIndex] = sort(a(2,:));
b = a(:,sortIndex);
How about
a = [1 4 5; 6 7 2]
a =
1 4 5
6 7 2
>> [s,idx] = sort(a(2,:))
s =
2 6 7
idx =
3 1 2
>> b = a(:,idx)
b =
5 1 4
2 6 7
in other words, you use the second argument of sort to get the sort order you want, and then you apply it to the whole thing.