## Animal.txt

### January 12, 2021

Today’s task is from a beginning programmer, who starts with an input file called animal.txt:

There once was a Dog

Wednesday he ate Apples
Thursday he ate Apples
Friday he ate Apples
Saturday he ate carrots

There once was a Bear

Tuesday he ate carrots
Wednesday he ate carrots
Thursday he ate chicken

He wants to create this output:

Food: Apples Animals who ate it: 1
=======
Dog

Food: Carrots Animals who ate it: 2
========
Bear
Dog

Food: Chicken Animals who ate it: 1
========
Bear

He gave a complicated awk solution that didn’t work; it produced duplicate lines of output in those cases
where the same animal ate the same food on multiple days.

Your task is to write a program to produces the desired transformation form input to output. When
you are finished, you are welcome to read or run a suggested solution, or to post your own solution or
discuss the exercise in the comments below.

Pages: 1 2

### 9 Responses to “Animal.txt”

1. Zack said

A cute little exercise. I hope it’s not counted as cheating if I tackle it using Julia (1.5.2): https://pastebin.com/UDbyNBhQ

I really had no idea that bears ate carrots!

2. Daniel said

Here’s a solution in Python.

import sys
from collections import defaultdict

lookup = defaultdict(set)  # maps foods to a set of animals

with open(sys.argv[1]) as f:
line = line.strip()
if line.startswith('The'):  # 'The' identifies 'There once ...'
animal = line[17:].capitalize()  # len('There once was a ') == 17
elif line:
food = line[line.index(' ') + 8:].capitalize()  # len(' he ate ') == 8

for idx, (food, animals) in enumerate(lookup.items()):
print(f'Food: {food} Animals who ate it: {len(animals)}')
print('=======')
for animal in animals:
print(animal)
if idx + 1 < len(lookup):
print()

Example usage:

\$ python3.9 animal.py animal.txt
Food: Apples Animals who ate it: 1
=======
Dog

Food: Carrots Animals who ate it: 2
=======
Bear
Dog

Food: Chicken Animals who ate it: 1
=======
Bear

3. esilence said

Python

import re

inFile = open(‘animal.txt’)

dt = dict()
l = set()
f = ”
for line in inFile:
if re.match(“There once was a”, line):
if f:
dt[f] = l
f = line.split()[-1].capitalize()
l = set()
elif re.search(“he ate”, line):
s = line.split()[-1].capitalize()
dt[f] = l

dtr = dict() #reverse dict
for k, v in dt.items():
for iv in v:
if iv in dtr:
else:
dtr[iv] = set([k])

for val in dtr:
print(‘\nFood: {} Animals who ate it: {}’.format(val, len(dtr[val])))
print(‘=======’)
for i in dtr[val]:
print(i)

4. Globules said

Here’s an AWK version. The food names are capitalized, and the output sorted, to match the example.

/There once was a/ {
animal = \$5
}

/he ate/ {
foods[\$4][animal] = 1
}

END {
PROCINFO["sorted_in"] = "@val_str_asc"
for (f in foods) {
printf "Food: %s Animals who ate it: %d\n",
raiseCase(f), length(foods[f])
printf "========\n"
for (a in foods[f]) {
printf "%s\n", a
}
printf "\n"
}
}

function raiseCase(s) {
}

\$ awk -f animal_food.awk < animal_food.in.txt
Food: Apples Animals who ate it: 1
========
Dog

Food: Carrots Animals who ate it: 2
========
Bear
Dog

Food: Chicken Animals who ate it: 1
========
Bear

5. denpashogai said

Here’s a solution in Haskell using the cool Parsec module in the standard library. I noticed the capitalisation of animals and foods was inconsistent in the example input but I’m just ignoring that. Also it’s not quite obvious how to format code in these comments, so this might look terrible.

module Main where

import Text.Printf (printf)
import Data.List (intercalate, union)
import Data.Map (fromListWith, toList)
import Text.Parsec

main = do
(Right foods) <- parse parser "" <\$> getContents
let s = map (\(food,animals) -> unlines \$
[printf "Food: %s Animals who ate it: %d" food (length animals)
,"======="] ++ animals) \$ toList foods
putStrLn \$ intercalate "\n" s

parser = fromListWith union . concat <\$> foodAnimals `sepBy` newline

foodAnimals = do
string "There once was a "
animal <- many1 letter
many space
(do many1 letter
string " he ate "
food <- many1 letter
return (food, [animal])) `endBy` newline

6. denpashogai said

(and then I saw the “HOWTO: Posting Source Code” link right at the top of the page. On the plus side, it seems that the “code” tag suffices in place of “sourcecode” and the “lang” property can be omitted for unsupported languages like Haskell)

7. cagisw said

Here a solution in Java:

package codekatas;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.TreeSet;

public class AnimalFood {

class Animal implements Comparable<Animal>{

public Animal() {
food = new TreeSet<String>();
}

String name;
TreeSet<String> food;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public TreeSet<String> getFood() {
return food;
}

public void setFood(TreeSet<String> food) {
this.food = food;
}

@Override
public String toString() {
return "Animal [name=" + name + ", food=" + food + "]";
}

public int compareTo(Animal o) {
return this.name.compareTo(o.getName());
}
}

public static void main(String[] args) {

try {

AnimalFood outerObject = new AnimalFood();
TreeSet<Animal> animals = new TreeSet<Animal>();
AnimalFood.Animal innerObject = null;

"D:\\WORK\\workspace\\code_kata\\codekatas\\src\\main\\resources\\input\\Animal.txt");
String checkAnimal = "There once was a ";
String ate = " he ate ";
while (data != -1) {
}
fw.close();

for (String line : lines) {
if (line.toString().contains(checkAnimal)) {
String[] split = line.split(checkAnimal);
innerObject = outerObject.new Animal();
innerObject.setName(split[1].trim());
}
if (line.contains(ate)) {
String food = line.split(ate)[1];

}
}

TreeSet<String> fruits = new TreeSet<String>();
for (Animal row : animals) {
}

for (String fruit : fruits) {
System.out.println("Food: " + fruit + " Animals who ate it: " + count(animals, fruit));
System.out.println("========");
printAnimal(animals, fruit);
System.out.println("");
}

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

}

private static int count(TreeSet<Animal> animals, String fruit) {
int result = 0;
for (Animal animal : animals) {
if (animal.getFood().contains(fruit)) {
result++;
}
}
return result;
}

private static int printAnimal(TreeSet<Animal> animals, String fruit) {
int result = 0;
for (Animal animal : animals) {
if (animal.getFood().contains(fruit)) {
System.out.println(animal.getName());
}
}
return result;
}

}

8. cagisw said

Here a solution in Java:

package codekatas;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.TreeSet;

public class AnimalFood {

class Animal implements Comparable<Animal>{

public Animal() {
food = new TreeSet<String>();
}

String name;
TreeSet<String> food;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public TreeSet<String> getFood() {
return food;
}

public void setFood(TreeSet<String> food) {
this.food = food;
}

@Override
public String toString() {
return "Animal [name=" + name + ", food=" + food + "]";
}

public int compareTo(Animal o) {
return this.name.compareTo(o.getName());
}
}

public static void main(String[] args) {

try {

AnimalFood outerObject = new AnimalFood();
TreeSet<Animal> animals = new TreeSet<Animal>();
AnimalFood.Animal innerObject = null;

"D:\\WORK\\workspace\\code_kata\\codekatas\\src\\main\\resources\\input\\Animal.txt");
String checkAnimal = "There once was a ";
String ate = " he ate ";
while (data != -1) {
}
fw.close();

for (String line : lines) {
if (line.toString().contains(checkAnimal)) {
String[] split = line.split(checkAnimal);
innerObject = outerObject.new Animal();
innerObject.setName(split[1].trim());
}
if (line.contains(ate)) {
String food = line.split(ate)[1];

}
}

TreeSet<String> fruits = new TreeSet<String>();
for (Animal row : animals) {
}

for (String fruit : fruits) {
System.out.println("Food: " + fruit + " Animals who ate it: " + count(animals, fruit));
System.out.println("========");
printAnimal(animals, fruit);
System.out.println("");
}

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

}

private static int count(TreeSet<Animal> animals, String fruit) {
int result = 0;
for (Animal animal : animals) {
if (animal.getFood().contains(fruit)) {
result++;
}
}
return result;
}

private static int printAnimal(TreeSet<Animal> animals, String fruit) {
int result = 0;
for (Animal animal : animals) {
if (animal.getFood().contains(fruit)) {
System.out.println(animal.getName());
}
}
return result;
}
}