A small puzzle for you
This had me stumped for a bit, but I was quite pleased when I came up with a relatively simple solution.
Given several arrays, each of which has elements which are a subset of allowed elements, and given that every allowed element appears at least once in each array, how do I rewrite all arrays such that each element of each array which has an identical value to an element in another array has the same index number in each array, with missing elements being undef
?
OK, that was a mouthful. Here's an example which should make it clear:
@a = ( 'b', 'c', 'f' );
@b = ( 'a', 'd' );
@c = ( 'c', 'd', 'e' );
I should have the following when I'm done:
@a = ( undef, 'b', 'c', undef, undef, 'f' );
@b = ( 'a', undef, undef, 'd', undef, undef );
@c = ( undef, undef, 'c', 'd', 'e', undef );
In other words, I'm trying to line up all of those values (because they're going to an HTML table and my $client needed to see the missing values). Once you see the answer to the puzzle, it's actually not too hard.
Post your solutions below!
There's probably a nicer solution in case you don't want to stringify the elements, but this one seems simple and effective enough.
Here's my solution: https://gist.github.com/moritz/4421c5e9ab0e5d994bac
Mine is fairly similar to Moritz's: https://gist.github.com/jleader/fefa388194aad71193f9
Hi Ovid, Not sure if you are looking for an in place insertion of those 'undefs'.. but in case you are expending extra hash, then this would look much simple.
http://pastebin.com/XPwpwwTy
Something like this looks more logical and straightforward to me:
$VAR1 = { 'e' => [ undef, undef, undef, undef, undef, undef, undef, 1 ], 'c' => [ undef, 1, undef, undef, undef, 1, undef, undef ],
Here's my solution: http://codepad.org/Gz2cIoAb
Don't forget to package the solution to a module, for the rest of us :)
In your example the input lists are sorted. Is that part of the problem spec? If so, you can do it by shifting off the input lists one position at a time:
This is a lot more verbose than the solutions with uniq or hashes, but I believe it takes linear time in the length of the inputs, while they take order (n log n).
Kinda different approach.
https://gist.github.com/choroba/4be98adb9425bd40ba0d
Untested. Downside is the double copy.
That would be nice if this were C, but it’s Perl. So the algorithm may be linear, but due to huge amounts of op dispatch, sub call overhead etc, its implementation in Perl likely has a humongous constant factor, which a big-O analysis hides. In order to actually see it outrun the algorithms that are worse in big-O terms, you’ll probably have to feed it inputs on the order of six digits, maybe even seven or more.
Optimising Perl code usually means finding a builtin to push the bulk of the work into, even if that means an overall algorithm that scales worse to very large inputs.
Aristotle, you are right of course that in practice code based on builtins is likely to be faster. The input size would have to be quite huge before the list-popping implementation starts to outperform one based on hashes or sorting.
My answer is buggy in any case: try
@lists = (["a", "c", "f", "j"], ["i", "j"], []);
It puts "j" in two different columns.
Not sure if it had to be in place or sorted, but I believe this works.
With apologies to Moritz, whose framework I stole. I would have used Aristotle's uniq replacement, but $work perl is only 5.10.1
Mine was pretty bad compared to others...
Yet another solution http://pastebin.com/7HSB8YTW
I assume that the goal solution will not keep initial order and the expectation is to remove duplication in the process. If so here's another way to solve this one:
https://gist.github.com/notbenh/866b95cbea06a5d50b4f
Using the example in full:-