DEV Community

Cesar Del rio
Cesar Del rio

Posted on • Updated on

#7 - Closest and Smallest CodeWars Kata (5 kyu)

Instructions:

Input
a string strng of n positive numbers (n = 0 or n >= 2)
Let us call weight of a number the sum of its digits. For example 99 will have "weight" 18, 100 will have "weight" 1.

Two numbers are "close" if the difference of their weights is small.

Task:
For each number in strng calculate its "weight" and then find two numbers of strng that have:

the smallest difference of weights ie that are the closest
with the smallest weights
and with the smallest indices (or ranks, numbered from 0) in strng

Output:
an array of two arrays, each subarray in the following format:
[number-weight, index in strng of the corresponding number, original corresponding number in strng]

or a pair of two subarrays (Haskell, Clojure, FSharp) or an array of tuples (Elixir, C++)

or a (char*) in C or a string in some other languages mimicking an array of two subarrays or a string

or a matrix in R (2 rows, 3 columns, no columns names)

The two subarrays are sorted in ascending order by their number weights if these weights are different, by their indexes in the string if they have the same weights.

Examples:

Let us call that function closest

strng = "103 123 4444 99 2000"
the weights are 4, 6, 16, 18, 2 (ie 2, 4, 6, 16, 18)
closest should return [2, 4, 2000], [4, 0, 103]
because 2000 and 103 have for weight 2 and 4, their indexes in strng are 4 and 0.
The smallest difference is 2.
4 (for 103) and 6 (for 123) have a difference of 2 too but they are not
the smallest ones with a difference of 2 between their weights.
....................

strng = "80 71 62 53"
All the weights are 8.
closest should return [[8, 0, 80], [8, 1, 71]]
71 and 62 have also:

  • the smallest weights (which is 8 for all)
  • the smallest difference of weights (which is 0 for all pairs)
  • but not the smallest indices in strng. ....................

strng = "444 2000 445 544"
the weights are 12, 2, 13, 13 (ie 2, 12, 13, 13)
closest should return [[13, 2, 445], [13, 3, 544]] or ([13, 2, 445], [13, 3, 544])
or [{13, 2, 445}, {13, 3, 544}] or ...
444 and 2000 have the smallest weights (12 and 2) but not the smallest difference of weights;
they are not the closest.
Here the smallest difference is 0 and in the result the indexes are in ascending order.
...................

closest("444 2000 445 644 2001 1002") --> [[3, 4, 2001], [3, 5, 1002]] or ([3, 4, 2001],
[3, 5, 1002]]) or [{3, 4, 2001}, {3, 5, 1002}] or ...
Here the smallest difference is 0 and in the result the indexes are in ascending order.
...................

closest("239382 162 254765 182 485944 468751 49780 108 54")
The weights are: 27, 9, 29, 11, 34, 31, 28, 9, 9.
closest should return [[9, 1, 162], [9, 7, 108]] or ([9, 1, 162], [9, 7, 108])
or [{9, 1, 162}, {9, 7, 108}] or ...
108 and 54 have the smallest difference of weights too, they also have
the smallest weights but they don't have the smallest ranks in the original string.
..................

closest("54 239382 162 254765 182 485944 468751 49780 108")
closest should return [[9, 0, 54], [9, 2, 162]] or ([9, 0, 54], [9, 2, 162])
or [{9, 0, 54}, {9, 2, 162}] or ...

My solution:

function  closest(strng) {
//   Example DATA: strng = "55 44 33 22 11"


//   -------------------- DATA CONVERTIONS ---------------------

  let strngNumbers = strng.split(' ')
  //   It returns: ['55', '44', '33', '22'. '11']


  let numbers = strngNumbers.map(n=> parseInt(n) )
  //   It returns: [55, 44, 33, 22. 11]


  let dividedNumbers = strngNumbers.map(n=>n.split('').map(d=>parseInt(d)))  
  //   It returns: [[5,5], [4,4], [3,3], [2,2]. [1,1]]


  let weigths = dividedNumbers.map(n=>n.reduce((acc,el)=> acc+=el , 0))
  //   It returns: [10, 8, 6, 4, 2]


  let orderedWeigths = weigths.slice().sort((a, b) => a - b);
  //   It returns: [2, 4, 6, 8, 10]


  let weigthDifference = [];
      for(let i=0; i < orderedWeigths.length - 1; i++){
        let dif = orderedWeigths[i+1] - orderedWeigths[i] 
          weigthDifference.push(dif)  
      }
  //   It returns: [4-2, 6-4, 8-6, , 10-8]
  //   That is equal to: [2, 2, 2, 2, 2]


//    -------------------- RESULT DATA ---------------------
  let smallestDifferenceIndex = weigthDifference.indexOf(Math.min(...weigthDifference))
  //   It returns: 0


  let smallestWeigths = [orderedWeigths[smallestDifferenceIndex], orderedWeigths[smallestDifferenceIndex + 1]]
  //   It returns: [2, 4]


  let smallestWeigthIndex

      if(smallestWeigths[0] == smallestWeigths[1])
        {
          smallestWeigthIndex =  
            [
              weigths.indexOf(smallestWeigths[0]), 
              weigths.indexOf(smallestWeigths[1], weigths.indexOf(smallestWeigths[0]) + 1)
            ]
        }

      else
        {
          smallestWeigthIndex = [ weigths.indexOf(smallestWeigths[0]), weigths.indexOf(smallestWeigths[1])]
        }
  //   It returns: [0,1]


  let originalNumber = [numbers[smallestWeigthIndex[0]], numbers[smallestWeigthIndex[1]]]
  //   It returns: [55,44]


//   -----------------------------------------  GETTING THE RESULT
  let result = [
    [
      smallestWeigths[0],
      smallestWeigthIndex[0],
      originalNumber[0]
    ],

    [
      smallestWeigths[1],
      smallestWeigthIndex[1],
      originalNumber[1]
    ]
  ]
  //   It returns: [[2,0,2], [4,1,4]]


    if(strng == ""){
      return []
    }else{
      return result
    }
}

Enter fullscreen mode Exit fullscreen mode

Explanation

1. Data convertions

First I started converting the string that the parameter of the function gave me so I could have everything I needed to get the result.

strngNumbers
I started spliting the string breaking it from the spaces so I could get the numbers divided on an array.

Input: "55 44 33 22 11"
Output: ['55', '44', '33', '22'. '11']


numbers
Then I mapped that array so I could change every string to a number.

Input: ['55', '44', '33', '22'. '11']
Output: [55, 44, 33, 22. 11]


dividedNumbers
Then I made an array with the digits of every number.

Input: [55, 44, 33, 22. 11]
Output: [[5,5], [4,4], [3,3], [2,2]. [1,1]]


weigths
Then I mapped the array of digits, and with every array I used .reduce() so I could get the sum of the digits and get the weigth

Input: [[5,5], [4,4], [3,3], [2,2]. [1,1]]
Output: [10, 8, 6, 4, 2]


orderedWeigths
Then I ordered the numbers from smaller to bigger using .slice() before .sort() so I get another array and the weigths variable isn't changed and I could use it later

Input: [[5,5], [4,4], [3,3], [2,2]. [1,1]]
Output: [2, 4, 6, 8, 10]


weigthDifference
Then I used a for loop that took these ordered Weigths Array and returned the weigth difference between a number and the number that is before of it in the aray index positions.

It does this action: [4-2, 6-4, 8-6, , 10-8]
It returns: [2, 2, 2, 2, 2]


smallestDifferenceIndex
After that I used Math.min() on the array of weigths difference so I get the smallest weigths difference and the I searched for the index of that difference on the differences array

Input: [2, 2, 2, 2, 2]
Output: 0

GETTING THE RESULT DATA

smallestWeigths
After that for getting the smallest Weigths with the smalles index I just made an array with the values of the Ordered weigths with the smallest Index of the difference so I could get the real smallest weigth and then I did the same but with the smallest index + 1 so I could get the consecutive value

Like this:
let smallestWeigths = [orderedWeigths[smallestDifferenceIndex], orderedWeigths[smallestDifferenceIndex + 1]]


smallestWeigthIndex
After that I searched for the weigth index data, for this I used a conditional for knowing if both smallest weigths were equal, so if they were equal, with the .indexOf() I had to use the second parameter that indicates from wich index will it start searching so it doesn't give me the same index value, it'll search after the last index value + 1

Code:

if(smallestWeigths[0] == smallestWeigths[1])
        {
          smallestWeigthIndex =  
            [
              weigths.indexOf(smallestWeigths[0]), 
              weigths.indexOf(smallestWeigths[1], weigths.indexOf(smallestWeigths[0]) + 1)
            ]
        }

      else
        {
          smallestWeigthIndex = [ weigths.indexOf(smallestWeigths[0]), weigths.indexOf(smallestWeigths[1])]
        }

Enter fullscreen mode Exit fullscreen mode

Output: [0, 1]


originalNumber
Then I have to get the original number in the first array, with the same index value.

Array I have to search on: [55, 44, 33, 22. 11]

So I just used the numbers variable where the orginal numbers where, and I used the smallestWeigthIndex array to take the values that are on the same index of the smallest weigths ones but from the original numbers array

let originalNumber = [numbers[smallestWeigthIndex[0]], numbers[smallestWeigthIndex[1]]]
Enter fullscreen mode Exit fullscreen mode

Output: [55,44]


SHOWING THE RESULT

result
For showing the result I did a array with 2 arrays inside of it, because thats how the result has to be shown, inside of every array I put the values in the order that the Kata asks for, that with the arrays of the result Data section

  let result = [
    [
      smallestWeigths[0],
      smallestWeigthIndex[0],
      originalNumber[0]
    ],

    [
      smallestWeigths[1],
      smallestWeigthIndex[1],
      originalNumber[1]
    ]
  ]
Enter fullscreen mode Exit fullscreen mode

Output: [[2,0,2], [4,1,4]]

And at the end I just used a conditional for if the string is empty it'll return an empty array if not it'll return the result

    if(strng == ""){
      return []
    }else{
      return result
    }
}
Enter fullscreen mode Exit fullscreen mode

What do you think about this solution? 👇🤔

Follow me on twitter
My Github
Solve this Kata

Discussion (0)