Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

What is the easiest way to check if column A and B values goes both ways?

Output to check:

Mike John
John Mike
Pamela Barbara
Barbara Pamela
Mike Paul
Roger Paul

Desired output

Mike <-> John
Pamela <-> Barbara
Mike -> Paul
Roger -> Paul

PS.

First it could be like looking all the possible values in A and B column, then doing wordcount for each line

Mike John 1 1
Pamela Barbara 1 1
Mike Paul 1 0 
Roger Paul 1 0

Then change is output to desired one.

share|improve this question
1  
This doesn't really seem to have anything to do with Unix or Linux, especially with the python, awk, and perl tags. Have you considered Stack Overflow? Though I think they'll want to see what you've tried first. –  derobert Sep 12 '12 at 15:37
add comment

closed as off topic by derobert, Renan, warl0ck, jw013, Mat Oct 19 '12 at 5:38

Questions on Unix & Linux Stack Exchange are expected to relate to Unix or Linux within the scope defined by the community. Consider editing the question or leaving comments for improvement if you believe the question can be reworded to fit within the scope. Read more about reopening questions here.If this question can be reworded to fit the rules in the help center, please edit the question.

6 Answers

If order isn't an issue, you could use this hash solution in awk.

BEGIN { d = "|" }
{
  if(names[$2 d $1] > 0)
    ++names[$2 d $1]
  else
    ++names[$1 d $2]
}

END { 
  for(n in names) { 
    split(n, a, d)
    if(names[n] >= 2)
      print a[1] " <-> " a[2]

    if(names[n] == 1)
      print a[1] " -> " a[2]
  }
}

The hash value gets initialized to the concatenation of the two names delimited by a pipe (the d variable); if those names occur again in opposite order, that particular element in the hash gets incremented to 2.

Output:

Pamela <-> Barbara
Mike -> Paul
Roger -> Paul
Mike <-> John
share|improve this answer
add comment

Well, since you've tagged this , despite your title:

#!/usr/bin/env python

input_file    = 'input.dat'    
out_data      = []
relationships = []

in_fh = open(input_file, 'r')
for line in in_fh:
    x, y = line.split()

    # If the reverse mapping was already seen...
    if (y, x) in out_data:    
        # ... then update the reverse mapping to point both ways
        idx = out_data.index( (y, x) )
        relationships[idx] = '<->'

    # Otherwise, we have no reverse mapping yet...
    else:
        # if we haven't seen the forward mapping yet either...
        if (x, y) not in out_data:    
            # ...then record the forward mapping
            out_data.append( (x, y) )
            relationships.append('->')

in_fh.close()    

# Print the final mappings
for (x, y), arrow in zip(out_data, relationships):
    print "%s %s %s" % (x, arrow, y)
share|improve this answer
add comment

This is my failed bash script attempt at avoiding using arrays or hashes. (bothways.txt file contains the example data).

#!/bin/bash

sourcefile=bothways.txt
reversefile=bothways2.txt
dupefile=bothways3.txt

# Create reverse file by swapping the columns
sed -r 's/(\w+)(\s+)(\w+)/\3\2\1/g' <$sourcefile >$reversefile

# Create dupe file by concatenating source and reverse files
# and displaying the duplicate lines
cat $sourcefile $reversefile | sort | uniq -d >$dupefile

while read line
do
    if grep "$line" $dupefile >/dev/null
    then
        arrow='<->';
    else 
        arrow='->';
    fi
    echo $line | sed -r "s/(\w+)\s+(\w+)/\1 $arrow \2/g"
done < $sourcefile

Output:

Mike <-> John
John <-> Mike
Pamela <-> Barbara
Barbara <-> Pamela
Mike -> Paul
Roger -> Paul

The problem with the output is that it contains redundant lines. (Although the relations are correct.)

share|improve this answer
add comment

This perl version expects input on stdin or from filenames(s) specified on command line, and prints results to stdout.

It uses a hash to keep track of how many times it has seen the same values together (ignoring subsequent lines unless they are in reverse order from the first). Twice means the relationship between column A and column B is bidirectional A<->B, while once means uni-directional from A->B.

Hashes are inherently unordered, so we can use the following array to preserve the order of input lines, and print the results in a similar order (won't be exactly the same because we only print AB even if BA was also seen)

If the order of the output is not important, then comment out or delete the my @order line and the push line, and swap the comment char on the two versions of the foreach lines below.

#! /usr/bin/perl 

use strict;

# hash keys will be 'columnA-columnB', we'll split them when we print the report.
my %AB=();
my @order=();

while(<>) {
    chomp;
    my ($A, $B) = split;

    # have we seen a key B-A?  if so, increment that rather than define A-B
    if (defined($AB{$B . '-' . $A})) {
        $AB{$B . '-' . $A} = 2;
    } elsif (! defined($AB{$A . '-' . $B})) {
        $AB{$A . '-' . $B} = 1;
        push @order, $A . '-' . $B;
    }
}

#foreach my $key (sort keys %AB) {
foreach my $key (@order) {
    my ($A,$B) = split /-/,$key;

    if ($AB{$key} == 1) {
        print "$A -> $B\n";
    } elsif ($AB{$key} == 2) {
        print "$A <-> $B\n";
    } ;
}

Output:

Mike <-> John
Pamela <-> Barbara
Mike -> Paul
Roger -> Paul
share|improve this answer
add comment

here the same algorithm in bash

#!/bin/bash

while read n1 n2; do
  n1=${n1//[^[:alpha:]]}
  n2=${n2//[^[:alpha:]]}
  n=___${n2}_$n1
  k=${!n}
  if ((k>0)); then
    ((___${n2}_$n1++))
  else
    ((___${n1}_$n2++))
  fi
done

for n in ${!___*}; do
  k=${!n}
  n=${n:3}
  if ((k>=2)); then
    echo "${n/_/ <-> }"
  elif ((k==1)); then
    echo "${n/_/ -> }"
  fi
done
share|improve this answer
add comment

A python solution that works for your example data, giving the same output:

Unlike some other examples this remembers one line of input and produces output in order.

paddy$ more tmpt.txt tmpt.py 
::::::::::::::
tmpt.txt
::::::::::::::
Mike John
John Mike
Pamela Barbara
Barbara Pamela
Mike Paul
Roger Paul
::::::::::::::
tmpt.py
::::::::::::::
lastwords = []
with file('tmpt.txt') as f:
    for line in f:
        words = line.strip().split()
        if words[::-1] == lastwords:
            print(' <-> '.join(lastwords))
            words = []
        elif lastwords:
            print(' -> '.join(lastwords))
        lastwords = words
if lastwords:
    print(' -> '.join(lastwords))

paddy$ python tmpt.py
Mike <-> John
Pamela <-> Barbara
Mike -> Paul
Roger -> Paul
paddy$ 
share|improve this answer
add comment

Not the answer you're looking for? Browse other questions tagged or ask your own question.