Need help in comparing multiple columns from two files.

Hi all,

I have two files as below. I need to compare field 2 of file 1 against field 1 of file 2 and field 5 of file 1 against filed 2 of file 2. If both matches , then create a result file 1 with first file data and if not matches , then create file with first fie data. Please help me in achieving this.

 awk 'NR=FNR{a[NR]=$0;next} { split ( a[$1],x, " ") if ( ( $2 ~ x[1] ) and ( $5 ~ x[2] ) ) { print $0 > "yes.txt"; } else { print $0 > " no.txt" } ; } file2 file1 2>> log.txt  

File1 :

1234 chennai kovai 1256 blr
2331 Madras Coimbatore 3234 Bangalore
5467 Chennai city 2456 ooty

File 2:

Madras Bangalore
Chennai blr

Result file 1 :- yes.txt

1234 chennai kovai 1256 blr 

Result file 2:- no.txt

2331 Madras Coimbatore  3234 Bangalore 
5467 Chennai city 2456 ooty

Please use code tags - not ICODE tags - for code AND DATA as required by forum rules!

I'm a bit surprised by your result files - Madras Bangalore should show up in yes.txt as it perfectly fits, and Chennai blr should not as the upper case C does not exist in file1.

Trying to adapt your code snippet as far as possible and correcting your input files, I came up with

awk     'NR==FNR                {a[$1,$2]; next}
         ($2 SUBSEP $5 in a)    {print $0 > "yes.txt"; next}
                                {print $0 > "no.txt" }
        ' file2 file1
cf *.txt
no.txt:
5467 Chennai city 2456 ooty
yes.txt:
1234 Chennai kovai 1256 blr
2331 Madras Coimbatore 3234 Bangalore

Pls check if you can live with that.

1 Like

why 2331 Madras Coimbatore 3234 Bangalore is not in yes.txt file ? What kind of comparison you are doing case sensitive or insensitive?

If its typo try this similar to the one RudiC suggusted

akshay@nio:/tmp$ cat f1
1234 chennai kovai 1256 blr
2331 Madras Coimbatore 3234 Bangalore
5467 Chennai city 2456 ooty
akshay@nio:/tmp$ cat f2
Madras Bangalore
Chennai blr
akshay@nio:/tmp$ awk 'FNR==NR{A[tolower($1 FS $2)];next}{print >sprintf("%s.txt",(tolower($2 FS $5) in A ? "yes" : "no"))}' f2 f1
akshay@nio:/tmp$ cat yes.txt 
1234 chennai kovai 1256 blr
2331 Madras Coimbatore 3234 Bangalore
akshay@nio:/tmp$ cat no.txt 
5467 Chennai city 2456 ooty
1 Like

Untested, but seems close to what you're trying to do:

awk '
NR == FNR {
        a[tolower($1)] = tolower($2)
        next
}
tolower($2) in a && a[tolower($2)] == tolower($5) {
        print > "yes.txt"
        next
}
{       print > "no.txt"
}' file2 file1 2>> log.txt
1 Like

Thank you rudiC and Aksay for such a quick reply..
Yes, the files are case sensitive and I did a typo mistake..

Now the actual issue over here is file 1 will have 20 million records and file 2 will have 30 thousand records... So will it cause performance issue if we go with this comparison?

Earlier I have achieved this with two while loops however it took hell lot of time. So now came to awk part.. Actually the if condition will be checking like below..

    if ( ( $2 ~ x[1] ) and ( $5 ~ x[2] ) )  or (( $2 ~ x[2] ) and ( $5 ~ x[1] )) { print $0 > "yes.txt"; } else { print $0 > " no.txt" } ; } file2 file1 2>> log.txt  

Will this can be implemented there

---------- Post updated at 04:46 PM ---------- Previous update was at 04:33 PM ----------

Hi don,
Thank you...

Will this gives me a performance issues with changed if condition and huge volume of files.

The suggestion I provided was first posted in your other thread where the example you provided seemed to require case insensitive matching.

Now that you have clearly specified that you want case sensitive matching, RudiC's suggestion seems to be what you need (assuming you don't really want a space in the filename no.txt ). Although the sample code you provided used ~ for matching instead of testing for equality; so I'm not sure what you mean by match. If file2 contains the line:

York  oo

should that match the line:

123 Yorkshire x y boot

in file1 in addition to matching the line:

456 York a b oo

? If so, RudiC's suggestion won't work, and anything we come up with will be significantly slower.

You say that you have code that works, but runs too slow; but you haven't shown us any code that would work at all. So, it is hard to guess at how many orders of magnitude faster RudiC's suggestion will run than your current code. If you have a shell script with a nested loop that is executing awk 600 billion times (30,000 * 20,000,000) and you want exact matches on fields (rather than substring matches), I wouldn't be surprised if RudiC's suggestion will run faster by a factor of well over a billion.

Why haven't you tried his code to see how well it works for you???

1 Like
  1. The elegant solution is to use tools that make delimited files act like SQL tables and do an SQL join.
  2. You can scan one file putting the concatenated keys into an associative array (awk, bash, ksh, etc.), then process the second file looking up the keys in the array. Associative arrays are hash lookup tables, keyed with strings.
  3. A more UNIX flavored solution is to cut out the columns to compare into two files or bash/ksh pipe streams '<(...)', sort them unique and match them with 'comm'. Now you have the key values to filter the input file. Sort the input file on the same columns so matching is a simple merge.
  4. If you create modified input files with the keys copied or moved on the front and then a delimiter and sort them, you can use 'join'. Since join only handles one join column, you need a different delimiter to make it join on multiple columns. If concatenating columns, make sure each column has values formatted all to the same length.

Make sure $LC_ALL=C during sort for binary order.

1 Like

Hi RudiC, The code which you gave is worked for my case. Thank U.
however i need to impelement the if as i mentioend in earlier post.

 if ( ( $2 ~ x[1] ) and ( $5 ~ x[2] ) )  or ( ( $2 ~ x[2] ) and ( $5 ~ x[1] ) ) { print $0 > "yes.txt"; } else { print $0 >

Please help on this

Above doesn't work. Neither awk nor shell have "and" or "or". Replace by "&&" and "||" and try again. Why do you "need to impelement the if" ? Was your specification not describing the whole picture?

RudiC gave you code that you say works. Now you want us to redo that working code to use code that is incomplete and contains many syntax errors.

WHY do you NEED to change what RudiC suggested?

Is this a homework assignment?

You implied earlier that you had code using nested loops that gives you the correct results but is too slow to work on your actual data. Please show us your working code!

And, please clarify what you mean by match. Do you mean the two strings being matched are equal? Or, do you mean that a string in file1 matches a string in file2 if the string in file1 is contained in the string in file2 ?

Thank you all for your valuable suggestions.. Some how the issue got resolved except the below one..

Now I need to print first column from file 2 and columns 1,2,4 from file 1 when ever I grt a match in below code...

awk     'NR==FNR                {a[$1,$2]; next}
         ($2 SUBSEP $5 in a)    {print a[$1], $1, $2,$4 > "yes.txt"; next}
                                {print $0 > "no.txt" }
        ' file2 file1

I am not able to get the result as expected. Please help

As per your specification, $1 in file2 equals $2 in file1. So you could print $2, $1, $2, $4 in your script.

Yes rudic..
You are correct.. I want to print first column from file 2 and some columns from file 1 when it matches...please help.