Саму задачу можно найти здесь.
Обратите внимание:
- В этой статье рассматривается только одно решение задачи, для которой существует несколько решений.
- Решение в полном объеме можно найти внизу.
- В решении используется главная функция, объекты, циклы, условия, методы массивов и метод
Object.values()
.
Поскольку реальный «входной текст головоломки» в задаче очень длинный, мы будем работать с примером «входного текста головоломки»:
abcdef
bababc
abbcde
abcccd
aabcdd
abcdee
ababab
Прежде чем перейти к основной части задачи, мы должны преобразовать то, что написано выше, в JavaScript, чтобы затем преобразовать его в массив строк (каждая строка будет идентификатором ящика).
Как же нам интерпретировать семь строк текста? Другими словами, как мы можем перевести (так сказать) их в JavaScript?
Ну, мы рассматриваем их как одну длинную строку с переносами строк:
'abcdefnbababcnabbcdenabcccdnaabcddnabcdeenababab'
Как мы можем убедиться, что это правильно?
Если мы выведем строку в консоль, то увидим, что она является исходным вводом:
abcdef
bababc
abbcde
abcccd
aabcdd
abcdee
ababab
Итак, мы знаем, что все правильно.
Давайте сохраним строку в переменную examplePuzzleInput
.
const examplePuzzleInput = 'abcdefnbababcnabbcdenabcccdnaabcddnabcdeenababab'
Далее, давайте…
- воспользуемся методом
split()
для преобразования длинной строки в массив строк - сохраним результат в переменной
boxIDsArr
.
const boxIDsArr = examplePuzzleInput.split('n')
Что записывает boxIDsArr
?
['abcdef', 'bababc', 'abbcde', 'abcccd', 'aabcdd', 'abcdee', 'ababab']
Итак, теперь у нас есть наш массив, и мы можем перейти к основной части задачи.
Наша задача состоит в том, чтобы…
- подсчитать количество идентификаторов ящиков, в которых есть ровно 2 любые буквы
- подсчитать количество идентификаторов ящиков, в которых ровно 3 любые буквы
- сделать это, чтобы получить контрольную сумму: количество двоек * количество троек.
(Если требуется больше ясности, сама задача содержит некоторые пояснения).
Сначала мы создадим функцию getMatches
, которая принимает параметр input
. Input
— это псевдоним, который мы будем использовать для массива идентификаторов ящиков, показанного выше.
function getMatches(input) {
}
Поскольку мы хотим подсчитать…
- количество идентификаторов ящиков, в которых есть ровно ДВЕ любые буквы
- количество идентификаторов ящиков, в которых есть ровно ТРИ любые буквы
…следующее, что мы сделаем, это создадим объект matches
, который будет вести подсчет количества найденных совпадений для каждой из этих двух категорий.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
}
Поскольку нам нужно получить доступ к каждой из строк массива input
, мы собираемся пройти через input
с помощью цикла forEach
. Каждое значение, к которому мы будем обращаться в этом цикле, является строкой идентификатора ящика, поэтому назовем его boxIdStr
.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
})
}
Но на самом деле нам нужно иметь доступ к каждой букве в каждой строке. Поэтому, поскольку мы уже находимся в каждой строке, мы собираемся пройтись по каждой строке с помощью еще одного цикла forEach
.
Обратите внимание, что мы применяем метод split()
к каждой строке, чтобы преобразовать строку в массив букв (каждая буква является строкой). Поэтому boxIdStr.split('')
для первой строки массива input
(первая строка: 'abcdef'
) выглядит следующим образом:
['a', 'b', 'c', 'd', 'e', 'f']
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
boxIdStr.split('').forEach(letter => {
})
})
}
Поскольку мы собираемся подсчитывать количество вхождений каждой буквы в каждой строке, следующее, что мы сделаем, это создадим еще один объект, на этот раз под названием counts
, который будет начинаться как пустой объект.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
let counts = {}
boxIdStr.split('').forEach(letter => {
})
})
}
Как мы будем подсчитывать количество вхождений каждой буквы в каждой строке?
В качестве примера возьмем вторую строку в массиве input
(вторая строка: 'bababc'
). После преобразования в массив букв он выглядит следующим образом:
['b', 'a', 'b', 'a', 'b', 'c']
Когда цикл переходит по 'b'
(первая буква в строке):
Если counts[letter]
(другими словами, counts['b']
) существует (другими словами, если объект counts
имеет ключ b
), то увеличиваем значение b
на 1. Но если counts[letter]
(другими словами, counts['b']
) еще не существует (другими словами, если объект counts
еще не имеет ключа b
), то дайте объекту counts
ключ b
и присвойте b
значение 1.
В приведенном ниже коде обратите внимание, что if
в предложении counts[letter]
является сокращением для counts.hasOwnProperty(letter)
, которое является сокращением для counts.hasOwnProperty(letter) === true
. Причина, по которой counts[letter]
работает здесь, заключается в том, что если он возвращает undefined
, то он возвращает что-то фальшивое
, а если он возвращает значение, то он возвращает что-то истинное
.
Также обратите внимание, что мы можем заменить приведенный ниже оператор if...else
на counts[letter] = (counts[letter] || 0) + 1
. Это просто более лаконичный способ добиться того же самого.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
let counts = {}
boxIdStr.split('').forEach(letter => {
if (counts[letter]) {
counts[letter]++
} else {
counts[letter] = 1
}
})
console.log(counts)
})
}
Обратите внимание, что console.log(counts)
включен в приведенный выше код только для того, чтобы мы могли видеть, как именно выглядит объект counts
для каждой строки после добавления к нему всего, что будет добавлено. Например, объект counts
для второй строки выглядит следующим образом:
{b: 3, a: 2, c: 1}
Таким образом, мы знаем, что во второй строке a
встречается два раза, b
— три раза, а c
— один раз.
Конечно, то, что мы исследовали здесь со второй строкой, повторяется для каждой строки в массиве input
.
Теперь нам нужно определить для каждого объекта counts
, содержит или не содержит он значения 2
или значения 3
. Обратите внимание, что мы не подсчитываем количество значений в каждом объекте counts
, которые являются 2
или 3
. (Если это различие все еще неясно, оно описано более подробно ниже).
Для этого мы используем метод includes()
.
Поскольку includes()
возвращает true
или false
, Object.values(counts).includes(2)
, например, не интересуется тем, сколько значений объекта counts
являются 2
, а скорее тем, есть ли у этого объекта counts
значения, которые являются 2
.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
let counts = {}
boxIdStr.split('').forEach(letter => {
if (counts[letter]) {
counts[letter]++
} else {
counts[letter] = 1
}
})
console.log(counts)
if (Object.values(counts).includes(2)) {
}
if (Object.values(counts).includes(3)) {
}
})
}
И если объект count
содержит значения 2
(независимо от того, сколько значений объекта 2
), мы увеличим значение ключа twice
объекта matches
на 1.
Другими словами, с таким объектом, как этот {a: 1, b: 2, c: 1, d: 1, e: 1}
, значение ключа matches
объекта twice
увеличится на 1, и это, вероятно, довольно очевидно. Но что может быть неочевидно, так это то, что даже с таким объектом, как этот {a: 2, b: 1, c: 1, d: 2}
, значение ключа twice
объекта matches
будет увеличиваться на 1, а не на 2. Опять же, это связано с тем, как работает includes()
.
И если объект count
содержит значения 3
(независимо от того, сколько значений объекта равно 3
), мы увеличим значение ключа matches
объекта thrice
на 1.
Другими словами, с таким объектом, как этот {a: 1, b: 1, c: 3, d: 1}
, значение ключа thrice
объекта matches
увеличится на 1, и это, вероятно, довольно очевидно. Но что может быть неочевидно, так это то, что даже с таким объектом, как этот {a: 3, b: 3}
, значение ключа thrice
объекта matches
будет увеличиваться на 1, а не на 2. Опять же, это связано с тем, как работает includes()
.
И это, вероятно, не нужно говорить, но, поскольку ясности больше, давайте включим это: С таким объектом, как этот {a: 2, b: 3, c: 1, d: 1, e: 3, f: 2, g: 3}
, значение ключей twice
и thrice
объекта matches
будет увеличено только на 1.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
let counts = {}
boxIdStr.split('').forEach(letter => {
if (counts[letter]) {
counts[letter]++
} else {
counts[letter] = 1
}
})
console.log(counts)
if (Object.values(counts).includes(2)) {
matches.twice++
}
if (Object.values(counts).includes(3)) {
matches.thrice++
}
})
}
Поскольку нам нужна контрольная сумма, а способ получения контрольной суммы заключается в вычислении количества двоек * количества троек, следующим шагом будет запись этого вычисления в нашей функции и возвращение его.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
let counts = {}
boxIdStr.split('').forEach(letter => {
if (counts[letter]) {
counts[letter]++
} else {
counts[letter] = 1
}
})
console.log(counts)
if (Object.values(counts).includes(2)) {
matches.twice++
}
if (Object.values(counts).includes(3)) {
matches.thrice++
}
})
return matches.twice * matches.thrice
}
Теперь наш последний шаг: Вызовите функцию getMatches
, передав ей boxIDsArr
, и запишите вызов этой функции в консоль.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
let counts = {}
boxIdStr.split('').forEach(letter => {
if (counts[letter]) {
counts[letter]++
} else {
counts[letter] = 1
}
})
console.log(counts)
if (Object.values(counts).includes(2)) {
matches.twice++
}
if (Object.values(counts).includes(3)) {
matches.thrice++
}
})
return matches.twice * matches.thrice
}
console.log(getMatches(boxIDsArr)) // 12 // 12 is the checksum
Надеюсь, это было понятно и полезно!
Спасибо за прочтение!