aboutsummaryrefslogtreecommitdiff
path: root/challenge-166/duncan-c-white/perl/ch-2.pl
blob: 46d4c8cdf9daf90e6406e658fbe2228b525107a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/usr/bin/perl
# 
# Task 2: K-Directory Diff
# 
# Given a few (three or more) directories (non-recursively), display a
# side-by-side difference of files that are missing from at least one of
# the directories. Do not display files that exist in every directory.
# 
# Since the task is non-recursive, if you encounter a subdirectory, append
# a /, but otherwise treat it the same as a regular file.
# 
# Example
# 
# Given the following directory structure:
# 
# dir_a:
# Arial.ttf  Comic_Sans.ttf  Georgia.ttf  Helvetica.ttf  Impact.otf  Verdana.ttf  Old_Fonts/
# 
# dir_b:
# Arial.ttf  Comic_Sans.ttf  Courier_New.ttf  Helvetica.ttf  Impact.otf  Tahoma.ttf  Verdana.ttf
# 
# dir_c:
# Arial.ttf  Courier_New.ttf  Helvetica.ttf  Impact.otf  Monaco.ttf  Verdana.ttf
# 
# The output should look similar to the following:
# 
# dir_a          | dir_b           | dir_c
# -------------- | --------------- | ---------------
# Comic_Sans.ttf | Comic_Sans.ttf  |
#                | Courier_New.ttf | Courier_New.ttf
# Georgia.ttf    |                 |
#                |                 | Monaco.ttf
# Old_Fonts/     |                 |
#                | Tahoma.ttf      |
# 
# 
# MY NOTES: this is much more appealing to me; right up my street.  Generally,
# it seems pretty simple, build a "fileindirs" mapping from file -> a list of
# which directories it's in, and form a set of all named files, then iterate
# over the "all named files" set, skipping files present in all the dirs,
# then iterating over all the dirs, using the fileindirs info to determine
# which columns to leave blank.  Need to auto-width each dir column, but that's
# trivial to do.
# 

use strict;
use warnings;
use feature 'say';
use Getopt::Long;
#use Function::Parameters;
use List::Util qw(max);
#use Data::Dumper;


my $debug=0;
die "Usage: k-diff [--debug] dir1 dir2 dir3..\n"
	unless GetOptions( "debug"=>\$debug ) && @ARGV>=3;

my @dirs = @ARGV;

my %allset;	# set of all files seen in any dir
my %filesindirs;# mapping of filename -> list of dirs it's in
my %dirwidth;   # map dirname -> max width of any filename

foreach my $dir (@dirs)
{
	opendir(my $dh, $dir) || die "Can't opendir $dir: $!\n";
	my @files = map { -d "$dir/$_" ? "$_/" : $_ }
		    grep { $_ ne "." && $_ ne ".." }
		    readdir($dh);
	$allset{$_}++ for @files;
	foreach my $file (@files)
	{
		$filesindirs{$file} //= [];
		my $aref = $filesindirs{$file};
		push @$aref, $dir;
	}
	$dirwidth{$dir} = max map { length($_)+1 } @files;
	closedir $dh;
}

my @cells;
my @dashes;
foreach my $dir (@dirs)
{
	my $w = $dirwidth{$dir};
	push @cells, sprintf( "%-${w}s", $dir );
	push @dashes, ('-' x $w);
}
say join( ' | ', @cells );
say join( '-|-', @dashes );

foreach my $file (sort keys %allset)
{
	my $aref = $filesindirs{$file};
	next if @$aref == @dirs;		# in all dirs
	@cells = ();
	my %indir = map { $_ => 1 } @$aref;	# 
	foreach my $dir (@dirs)
	{
		my $w = $dirwidth{$dir};
		my $str = $indir{$dir} ? $file : ' ';
		$str = sprintf( "%-${w}s", $str );
		push @cells, $str;
	}
	say join( ' | ', @cells );
}