Shorthand::sortWordsLexico()   A
last analyzed

Complexity

Conditions 3
Paths 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 6
cts 6
cp 1
rs 9.8666
c 0
b 0
f 0
cc 3
nc 1
nop 1
crap 3
1
<?php
2
3
namespace KamranAhmed\Shorthand;
4
5
use Exception;
6
7
/**
8
 * Class Shorthand
9
 *
10
 * @package KamranAhmed\Shorthand
11
 */
12
class Shorthand
13
{
14
    /**
15
     * @var array
16
     */
17
    private $words = [];
18
19
    /**
20
     * Shorthand constructor.
21
     *
22
     * @param array $words
23
     */
24 6
    public function __construct($words = [])
25
    {
26 6
        $this->setWords($words);
27 6
    }
28
29
    /**
30
     * @param  mixed $words
31
     *
32
     * @return void
33
     */
34 6
    public function setWords($words)
35
    {
36 6
        $this->words = !is_array($words) ? [$words] : $words;
37 6
    }
38
39
    /**
40
     * @return array
41
     *
42
     * @throws \Exception
43
     */
44 6
    public function generate()
45
    {
46 6
        if (empty($this->words)) {
47 1
            throw new Exception('Word(s) are required to generate shorthands');
48
        }
49
50
        // Sort them lexicographically, so that they're next to their nearest kin
51 5
        $words = $this->sortWordsLexico($this->words);
52
53 5
        $shorthands = [];
54 5
        $previous   = '';
55
56 5
        foreach ($words as $counter => $current) {
57
58
            // Get the next word
59 5
            $next = isset($words[$counter + 1]) ? $words[$counter + 1] : "";
60
61
            // Two matching words found, we cannot make any unique shorthands out of this one
62 5
            if ($current === $next) {
63 1
                continue;
64
            }
65
66
            // Get the index at which current word differs from the next and previous words
67 5
            $diffPoint = $this->getDiffPoint($current, $next, $previous);
68
69 5
            $previous = $current;
70
71
            // End of the current word has already been reached, just put the whole word
72
            // in the shorthands and continue as it means there was no difference
73
            // between the current word and the next/previous words
74 5
            if ($diffPoint === strlen($current)) {
75 1
                $shorthands[$current] = $current;
76
77 1
                continue;
78
            }
79
80
            // Generate the shorthands by creating substring from the diff point to the end
81 5
            while ($diffPoint <= strlen($current)) {
82 5
                $shorthand              = substr($current, 0, $diffPoint);
83 5
                $shorthands[$shorthand] = $current;
84
85 5
                $diffPoint++;
86 5
            }
87 5
        }
88
89 5
        return $shorthands;
90
    }
91
92
    /**
93
     * Gets the point at which current word differs from the next and previous words
94
     *
95
     * @param string $current
96
     * @param string $next
97
     * @param string $previous
98
     *
99
     * @return int
100
     */
101 5
    protected function getDiffPoint($current, $next, $previous)
102
    {
103 5
        $nextCharMatches = true;
104 5
        $prevCharMatches = true;
105
106 5
        $currentLength = strlen($current);
107
108
        // Iterate through each character of the current word
109
        // and find the point where it differs from the previous and next word
110 5
        for ($diffPoint = 0; $diffPoint < $currentLength; $diffPoint++) {
111
112 5
            $currChar = $current[$diffPoint];
113 5
            $nextChar = !empty($next[$diffPoint]) ? $next[$diffPoint] : '';
114 5
            $prevChar = !empty($previous[$diffPoint]) ? $previous[$diffPoint] : '';
115
116 5
            $nextCharMatches = $nextCharMatches && $currChar === $nextChar;
117 5
            $prevCharMatches = $prevCharMatches && $currChar === $prevChar;
118
119
            // If neither of the next and previous word characters match,
120
            // we have found ourselves the point at which both the words differ
121 5
            if (!$nextCharMatches && !$prevCharMatches) {
122 5
                $diffPoint++;
123
124 5
                break;
125
            }
126 4
        }
127
128 5
        return $diffPoint;
129
    }
130
131
    /**
132
     * Sorts the words lexicographically in order to bring the words closer to their kin
133
     *
134
     * @param  array $words
135
     *
136
     * @return array
137
     */
138
    protected function sortWordsLexico($words)
139
    {
140 5
        usort($words, function ($a, $b) {
141 4
            if ($a === $b) {
142 1
                return 0;
143
            }
144
145 4
            return $a > $b ? 1 : -1;
146 5
        });
147
148 5
        return $words;
149
    }
150
}
151