loading...

Daily Challenge #243 - Redacted!

thepracticaldev profile image dev.to staff ・1 min read

The first document has parts redacted, and the other one doesn't. But the clean document might be a fake! You need to compare the two documents and decide if it is possible they are the same or not. Return true if the two documents are possibly the same. Return false otherwise.

Rules

  • Each document is made of any visible characters, spaces, punctuation, and newlines \n
  • Any character might be redacted (except \n)
  • The redaction character is X
  • The redacted document is always the first one

Example

doc1 = "TOP SECRET:\nThe missile launch code for Sunday XXXXXXXXXX is:\nXXXXXXXXXXXXXXXXX"
doc2 = "TOP SECRET:\nThe missile launch code for Sunday 5th August is:\n7-ZERO-8X-ALPHA-1"
Documents look the same, therefore true.

Tests

doc1 = "The name of the mole is Professor XXXXX"
doc2 = "The name of the mole is Professor Dinglemouse"

doc1 = "XXXXXXXX XXXXXXX XXXXXXXXXXXXXXXXXXX\nXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXX XXXXXXXXXXXXX XXXXX"
doc2 = "Area-51. Medical Report. 23/Oct/1969\nE.T. subject 4 was given an asprin after reporting sick for duty today"

Good luck!


This challenge comes from dinglemouse on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Posted on by:

thepracticaldev profile

dev.to staff

@thepracticaldev

The hardworking team behind dev.to ❤️

Discussion

pic
Editor guide
 

Rust

Assuming that replacement with X is per character.

fn same(bad: &str, real: &str) -> bool {
    bad.len() == real.len()
        && bad
            .chars()
            .zip(real.chars())
            .all(|(b, r)| b == r || (b == 'X' && r != '\n'))
}

yay, a oneliner

With nightly feature

#![feature(iter_order_by)]
fn same(bad: &str, real: &str) -> bool {
    bad.len() == real.len()
        && bad
            .chars()
            .eq_by(real.chars(), |b, r| b == r || (b == 'X' && r != '\n'))
}

When you're a caveman and reimplement all:

fn same(bad: &str, real: &str) -> bool {
    if bad.len() != real.len() {
        return false;
    }
    for (b, r) in bad.chars().zip(real.chars()) {
        if b != r && (b != 'X' || r == '\n') {
            return false;
        }
    }
    true
}

When you're a caveman and reimplement .zip()

fn same(bad: &str, real: &str) -> bool {
    if bad.len() != real.len() {
        return false;
    }
    let mut bad = bad.chars();
    let mut real = real.chars();
    while let (Some(b), Some(r)) = (bad.next(), real.next()) {
        if b != r && (b != 'X' || r == '\n') {
            return false;
        }
    }
    true
}

(none of the latter ones give better performance, only worse)

 

Here is a C++ solution,

bool areSame(string doc1, string doc2){
    if(doc1.length() != doc2.length())
        return false;

    for(int i=0; i<doc1.length(); i++)
        if(doc1[i] != doc2[i] && (doc1[i] != 'X' || (doc1[i] == 'X' && doc2[i] == '\n')))
            return false;

    return true;
}
 

A javascript way. Although I think this could be done with regex...

function check(doc1, doc2)
{
  let docCheck = [...doc1].reduce((acc, cha, i) => 
  {
    if(cha.toLowerCase() === 'x')
    {
      acc += doc2[i];
    }
    else
    {
      acc += cha;
    }
    return acc;
  }, '');

  return docCheck === doc2;
}
 

This one can be slightly optimised to return early on failure and not go through the whole string:

const isSame = (a, b) => a.split('').every((char, index) => char === 'X' || char === b[index])
 

Implementation in Frink

same[redacted, original] :=
{
  Xval = char["X"]
  for x = zip[stringToBytes[redacted],stringToBytes[original]]
    if (x@0 != x@1) and (x@0 != Xval)
      return false
  return true
}
 

Dart, bit longer, but it's fine lol. I still abide by the rule that not everything needs to be creative. Readability is my philosophy. If you can read the code and understand what's happening, the better for everything reading the code. Personally.

void main() {
  var doc1 = "TOP SECRET:\nThe missile launch code for Sunday XXXXXXXXXX is:\nXXXXXXXXXXXXXXXXX";
  var doc2 = "TOP SECRET:\nThe missile launch code for Sunday 5th August is:\n7-ZERO-8X-ALPHA-1";
  print(allegedlySimilar(doc1, doc2)); // true

  doc1 = "The name of the mole is Professor XXXXX";
  doc2 = "The name of the mole is Professor Dinglemouse";
  print(allegedlySimilar(doc1, doc2));  // false

  doc1 = "XXXXXXXX XXXXXXX XXXXXXXXXXXXXXXXXXX\nXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXX XXXXXXXXXXXXX XXXXX";
  doc2 = "Area-51. Medical Report. 23/Oct/1969\nE.T. subject 4 was given an asprin after reporting sick for duty today";
  print(allegedlySimilar(doc1, doc2)); // true
}

bool allegedlySimilar(String doc1, String doc2) {
  var redacted = (data) {
    List<String> joined = List<String>();
    var lines = data.split("\n");
    for(var line in lines) {
      var data = StringBuffer();
      line.split('').forEach((char) => data.write('X'));
      joined.add(data.toString());
    }
    return joined.join("\n");
  };
  return redacted(doc1) == redacted(doc2);
}
 

Python 3, using regex'es:

# Solution
import re
match = lambda doc1, doc2: re.match(re.sub('X+', '.*?', doc1), doc2) is not None

# Test cases
cases = [
    ("TOP SECRET:\nThe missile launch code for Sunday XXXXXXXXXX is:\nXXXXXXXXXXXXXXXXX","TOP SECRET:\nThe missile launch code for Sunday 5th August is:\n7-ZERO-8X-ALPHA-1"),
    ("The name of the mole is Professor XXXXX", "The name of the mole is Professor Dinglemouse"),
    ("XXXXXXXX XXXXXXX XXXXXXXXXXXXXXXXXXX\nXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXX XXXXXXXXXXXXX XXXXX", "Area-51. Medical Report. 23/Oct/1969\nE.T. subject 4 was given an asprin after reporting sick for duty today")]

for case in cases:
    print(f"doc1: {re.sub('X+', '*', case[0])}\ndoc2: {case[1]}\nDoc's match: {match(*case)}")

Try it online!