I have a table file and I want to shuffle the rows of specific columns in Perl.
For example, I have this array:
a 1 b 2 c 3 d 4 e 5 f 6 and I want to shuffle the second column to get something like this:
a 2 b 1 c 3 d 4 e 5 f 6 Using List::Util::shuffle might be a good idea. I used a Schwartzian transform to create a list of random numbers, sort them, and insert the column data based on the array index.
use strict; use warnings; use feature 'say'; my @col; while (<DATA>) { push @col, [ split ]; } my @shuffled = map { $col[$_->[0]][1] } # map to @col values sort { $a->[1] <=> $b->[1] } # sort based on rand() value map { [ $_, rand() ] } # each index mapped into array of index and rand() 0 .. $#col; # list of indexes of @col for my $index (0 .. $#col) { say join " ", $col[$index][0], $shuffled[$index]; } __DATA__ a 1 b 2 c 3 d 4 e 5 f 6 I can use this script to do the job:
#!/usr/bin/env perl use strict; use warnings; use List::Util qw/shuffle/; my @c = split /,/, $ARGV[0]; $_-- for @c; shift; my @lines; my @v; while ( <> ) { my @items = split; $v[$.-1] = [@items[@c]]; $lines[$.-1] = [@items]; } my @order = shuffle (0..$#lines); for my $l (0..$#lines) { my @items = @{ $lines[$l] }; @items[@c] = @{ $lines[$order[$l]] }[@c]; print "@items\n"; } This script uses List::Util which is part of Perl core modules since perl v5.7.3: corelist List::Util
It can be launched with perl shuffle.pl 2 test.txt
$[ (perldoc.perl.org/perlvar#$%5B), and I saw that it no longer works starting from Perl v5.30.0 even without doing use v5.16, so I had to remove the use of $[=1 from my script :(.Demo code for a case when external modules are not permitted.
use strict; use warnings; use feature 'say'; my %data; while( <DATA> ) { my($l,$d) = split; $data{$l} = $d; } say '- Loaded --------------------'; say "$_ => $data{$_}" for sort keys %data; for( 0..5 ) { @data{ keys %data} = @{ shuffle([values %data]) }; say "-- $_ " . '-' x 24; say "$_ => $data{$_}" for sort keys %data; } sub shuffle { my $data = shift; my($seen, $r, $i); my $n = $#$data; for ( 0..$n ) { do { $i = int(rand($n+1)); } while defined $seen->{$i}; $seen->{$i} = 1; $r->[$_] = $data->[$i]; } return $r; } __DATA__ a 1 b 2 c 3 d 4 e 5 f 6 Output
- Loaded -------------------- a => 1 b => 2 c => 3 d => 4 e => 5 f => 6 -- 0 ------------------------ a => 5 b => 4 c => 2 d => 6 e => 1 f => 3 -- 1 ------------------------ a => 3 b => 6 c => 2 d => 4 e => 1 f => 5 -- 2 ------------------------ a => 4 b => 5 c => 6 d => 1 e => 3 f => 2 -- 3 ------------------------ a => 6 b => 4 c => 1 d => 2 e => 3 f => 5 -- 4 ------------------------ a => 3 b => 4 c => 6 d => 5 e => 1 f => 2 -- 5 ------------------------ a => 6 b => 5 c => 3 d => 4 e => 2 f => 1