This isn't an algorithmic problem. Therefore, I think it's quite simple. My solution was in PHP:
Spoiler:
PHP Code:
<?php
class GrouponFraud {
private $fraud;
public function __construct() {
// Index elements
define('ORDER_ID', 0);
define('DEAL_ID', 1);
define('EMAIL', 2);
define('STREET', 3);
define('CITY', 4);
define('STATE', 5);
define('ZIP', 6);
define('CC', 7);
define('ORDER', 8);
$this->fraud = array();
}
// Get the fraudulent cases
public function getFraud() {
ksort($this->fraud);
return $this->fraud;
}
// Whether or not an order is deemed fraudulent
public function isFraud($order, $subset) {
$count = 0;
foreach ( $subset as $sOrder ) {
if ( ( is_array( $order ) && is_array( $sOrder ) ) == false ) {
continue;
}
if ( $this->isFraud1($order, $sOrder) || $this->isFraud2($order, $sOrder) ) {
$this->fraud[intval($order[ORDER_ID])] = true;
$this->fraud[intval($sOrder[ORDER_ID])] = true;
return true;
}
}
return false;
}
// Let's check the first condition - same email, different credit card
private function isFraud1($order, $comparison) {
if ( $this->equalArray($order, $comparison) == true ) {
return false;
}
return $this->sameEmail( $order[EMAIL], $comparison[EMAIL] ) == true && ( $this->sameCC($order[CC], $comparison[CC]) == false );
}
// Let's check the second condition - same address/city/state/zip, different CC, regardless of emailo
private function isFraud2($order, $comparison) {
// If the two arrays are identical, we can just return false - they're the same order, no fraud here.
if ( $this->equalArray($order, $comparison) == true ) {
return false;
}
return ( $this->sameStreet($order[STREET], $comparison[STREET]) && $this->sameState( $order[STATE], $comparison[STATE] ) && $this->sameZip( $order[ZIP], $comparison[ZIP] ) && $this->sameCity($order[CITY], $comparison[CITY]) );
}
// Checks whether two credit cards are the same
private function sameCC($cc1, $cc2) {
return $this->cleanCC($cc1) == $this->cleanCC($cc2);
}
// Clean the credit card
private function cleanCC($cc) {
return preg_replace('/[^\d]/', '', $cc);
}
// Checks whether two orders have the same city
private function sameCity($city1, $city2) {
return $this->cleanCity($city1) == $this->cleanCity($city2);
}
// 'Clean' the city
private function cleanCity($city) {
return strtolower($city);
}
// Checks whether the zip code is the same for two order
private function sameZip($zip1, $zip2) {
return $this->cleanZip($zip1) == $this->cleanZip($zip2);
}
// 'Cleans' the zip code
private function cleanZip($zip) {
return preg_replace('/[^\d]/', '', $zip);
}
// Checks whether 2 orders have the same state
private function sameState($state1, $state2) {
return $this->cleanState($state1) == $this->cleanState($state2);
}
// 'Clean' the state
private function cleanState($state) {
$clean = strtolower($state);
$clean = $this->removeWhitespace($clean);
// Checks whether 2 orders have the same street
private function sameStreet($street1, $street2) {
return $this->cleanStreet($street1) == $this->cleanStreet($street2);
}
// 'Clean' the street
public function cleanStreet($street) {
$clean = strtolower($street);
$clean = $this->removeWhitespace($clean);
$clean .= PHP_EOL;
$clean = str_replace( array(' street' . PHP_EOL, ' road' . PHP_EOL), array(' st.', ' rd.'), $clean );
$clean = str_replace(PHP_EOL, '', $clean);
return $clean;
}
// Checks whether two arrays are identical
private function equalArray($arr1, $arr2) {
return ! array_diff( $arr1, $arr2 ) && ! array_diff( $arr2, $arr1 );
}
//private Checks whether or not they have the same email address
private function sameEmail($first, $second) {
return $this->cleanEmail($first) == $this->cleanEmail($second);
}
// 'Clean' an email (remove all attempts to trick it)
private function cleanEmail($email) {
$clean = strtolower($email);
$clean = $this->removeWhitespace($clean);
$username = current(explode("@", $clean));
$rest = str_replace($username, '', $clean); // Rest of the email portion
$username = str_replace('.', '', $username); // Remove full stops
$username = current(explode("+", $username, 2)); // Remove all plus signs