# DataWeave programming challenge #3: Count palindrome phrases using the Strings module

> Review each of the input phrases and find those that are palindromes. Then, retrieve the character count and sum the total characters.

- **Author:** Alex Martinez
- **Published:** Mar 7, 2023
- **Category:** Challenges
- **Tags:** MuleSoft, DataWeave
- **Source:** https://prostdev.com/post/dataweave-programming-challenge-3

---
## Series: DataWeave Programming Challenges (Part 3 of 8)

1. [DataWeave programming challenge #1: Add numbers separated by paragraphs and get the max number](https://prostdev.com/post/dataweave-programming-challenge-1)
2. [DataWeave programming challenge #2: Rock Paper Scissors game score system](https://prostdev.com/post/dataweave-programming-challenge-2)
3. DataWeave programming challenge #3: Count palindrome phrases using the Strings module (this post)
4. [DataWeave programming challenge #4: Solve the Tower of Hanoi mathematical puzzle](https://prostdev.com/post/dataweave-programming-challenge-4)
5. [DataWeave programming challenge #5: Reverse a phrase's words, but keep the punctuation](https://prostdev.com/post/dataweave-programming-challenge-5)
6. [DataWeave programming challenge #6: Using tail-recursion to get the factorial of a number](https://prostdev.com/post/dataweave-programming-challenge-6)
7. [DataWeave programming challenge #7: Modify certain values from a JSON structure](https://prostdev.com/post/dataweave-programming-challenge-7)
8. [DataWeave programming challenge #8: Sum all digits to get a 1-digit number](https://prostdev.com/post/dataweave-programming-challenge-8)

---

Try to solve this challenge on your own to maximize learning. We recommend you refer to the [DataWeave documentation](https://docs.mulesoft.com/dataweave/latest/) **only**. Try to avoid using Google or asking others so you can learn on your own and become a DataWeave expert!

> [!PLAYGROUND]
> [Solve on the Playground](https://dataweave.mulesoft.com/learn/playground?projectMethod=GHRepo&repo=alexandramartinez%2Fdataweave-challenges&path=challenges%2F3)

> [!TIP]
> For this challenge, we encourage you to make use of the [Strings module](https://docs.mulesoft.com/dataweave/latest/dw-strings)'s functions.

## Input

Consider the following input payload (can be of **txt** format):

```
this is not a palindrome
blue
Mr. Owl ate my metal worm
Was it a car or a cat I saw?
you're doing great!
mug
Rats live on no evil star
what's taking so long?
kayak
Live on time, emit no evil
Step on no pets
boat
Anna
2020/02/02
15/03/2001
01/02/2010
```

## Explanation of the problem

Each line of the input payload is considered a different phrase. Review each phrase and find those that are [palindromes](https://en.wikipedia.org/wiki/Palindrome). Notice that punctuation, spaces, or special characters should not be considered to ensure a phrase is a palindrome.

For example,

- **Anna** in reverse is **anna** - this is a palindrome.
- **2020/02/02** in reverse is **20200202** - this is a palindrome.
- **Mr. Owl ate my metal worm** in reverse is **mrowlatemymetalworm** - this is a palindrome.

After that, retrieve the character count of the **original** phrase. Including punctuation, spaces, and special characters.

For example,

- Anna is **4**.
- 2020/02/02 is **10**.
- Mr. Owl ate my metal worm is **25**.

Return the total sum of all the palindrome's character count.

## Expected output

In this case, the expected output would be:

```
148
```

The result for each of the phrases (whether they're palindrome or not) should be:

- false
- false
- true
- true
- false
- false
- true
- false
- true
- true
- true
- false
- true
- true
- false
- true

## Clues

If you're stuck with your solution, feel free to check out some of these clues to give you ideas on how to solve it!

### Clue #1

Read the documentation of the [Strings module](https://docs.mulesoft.com/dataweave/latest/dw-strings) to make yourself familiar with the existing functions.

### Clue #2

If you've been following the previous challenges, we've learned that `payload splitBy "\n"` can be replaced with the `lines()` function from the Strings module.

### Clue #3

Use the lower() function to make sure all the characters can be compared in lower-case.

### Clue #4

Instead of using map() to iterate through the string's characters, you can use mapString() from the Strings module.

### Clue #5

You don't need to use regex to find the alphanumeric characters. You can use isAlphanumeric() from the Strings module.

### Clue #6

Instead of using selectors, you can use the reverse() function from the Strings module.

### Clue #7

Use sizeOf() and sum() to retrieve the character count in an array and then sum the complete array of numbers.

## Answer

If you haven't solved this challenge yet, we encourage you to keep trying! It's ok if it's taking longer than you thought. We all have to start somewhere ✨ Check out the clues and read the docs before giving up. You got this!! 💙

There are many ways to solve this challenge, but you can find here one of our solutions so you can compare your result with us.

### Solution #1

```dataweave
%dw 2.0
import lines, isAlphanumeric, mapString, reverse from dw::core::Strings
output application/json
---
sum(lines(payload) map (line) -> do {
    var clearLine = lower(line) mapString (
        if (isAlphanumeric($)) $
        else ""
    )
    var isPalindrome = clearLine == reverse(clearLine)
    ---
    if (isPalindrome) sizeOf(line)
    else 0
})
```

Subscribe to receive notifications as soon as new content is published ✨

---

## Reader notes

**Kevin Medina** (Feb 23, 2024): 

```dataweave
%dw 2.0
import * from dw::core::Strings
import * from dw::core::Arrays
output application/json
fun getPalindromCount(text) = (lines(text) map ((sentence, index) -> do{
    var cleanedSentence = lower(sentence mapString if (!isAlphanumeric($)) "" else $)
    ---
    if(reverse(cleanedSentence) == cleanedSentence) sizeOf(sentence) else 0
}))sumBy $
---
getPalindromCount(payload)
```

**Kelvin Fernandes** (Oct 23, 2023): 

```dataweave
%dw 2.0

import * from dw::core::Strings

var payloadLines = lines(payload)
var payloadLinesOnlyLetterAndNumbers = payloadLines map (
    lower(
        $ replace /[^A-Za-z0-9]/ with ""
    )
)
var areTheyPalindromes = payloadLinesOnlyLetterAndNumbers map (
    $ == reverse($)
)
var onlyPalindromesLines = (areTheyPalindromes map (
    if($) ( payloadLines[$$] ) else ( null )
)) filter $ != null

output application/json
---
/* Expected output: 148 */
sizeOf(onlyPalindromesLines joinBy "")
```

**Victor Manuel Rodriguez** (Jul 11, 2023): 

```dataweave
%dw 2.0
import * from dw::core::Strings
fun isPalindrome(word) = word == reverse(word)
fun formatWord(word) = lower(word replace /[^\w\d]/ with "")

output application/json
---
sum(lines(payload) map(if(isPalindrome(formatWord($))) sizeOf($) else 0))
```

**shwetabh singh** (May 6, 2023): Here is my approach:

```dataweave
%dw 2.0
fun checkPalindrome(a)= do{
    import * from dw::core::Strings
    var originalString = lower(a) replace /[\/,.'?!\s]/ with ""
    var reverseString = reverse(originalString)
    ---
    if(reverseString == originalString) "Palindrome" else "Not Palindrome"

}
import * from dw::core::Strings
output application/json
---
sum((lines(payload) map ((item, index) ->{
    isPalindrome: checkPalindrome(item),
    size: sizeOf(item)
} ) filter $.isPalindrome == "Palindrome").size)
```

**tafkamw** (May 2, 2023): 

```dataweave
%dw 2.0
output application/json

import * from dw::core::Strings

var cleanString = (aString) ->
    lower (aString mapString if( !isAlphanumeric($)) "" else $)
---
lines(payload) map (
    sizeOf(
        if (cleanString($) == reverse(cleanString($))) $ else ""
    )
)
reduce ($$ + $)
```

**mysg147** (Apr 22, 2023): 

```dataweave
%dw 2.0
import * from dw::core::Strings
output application/json
fun str(inputstring) = lower(inputstring mapString if(isAlphanumeric($)) $ else "")
var x  = payload splitBy('\n')
var y = x map((val,in) ->
if(reverse(str(val)) == str(val)) sizeOf(val) else 0
)
---
sum(y)
```

**Sudiptaa** (Mar 28, 2023): 

```dataweave
%dw 2.0
output json
import * from dw::core::Strings

fun isPalindrome(s) = do {
    var str = lower(s) replace /[^a-zA-Z0-9]/ with ""
    var revStr = lower(reverse(s)) replace /[^a-zA-Z0-9]/ with ""
    ---
    str == revStr
}
---
sum((payload splitBy("\n") map
{

inputString : $,
countChars: sizeOf($),
isPalindrome: isPalindrome($)

} filter($.isPalindrome)).countChars)
```

**Monika Soni** (Mar 22, 2023): 

```dataweave
%dw 2.0
output application/json
import * from dw::core::Strings
fun charcterstring(str) =  lower(str mapString (if(isAlphanumeric($)) $ else "" ))
---
sum((lines(payload) map ( if (charcterstring($)== reverse(charcterstring($))) sizeOf($) else 0)))
```

**armandomejiam** (Mar 10, 2023): 👌 This was my first approach, I was avoiding the usage of regex... then I checked your solution and I remember the existence of "isAlphanumeric" function, so I refactored my solution to make it a bit more clean:

```dataweave
%dw 2.0
import * from dw::core::Strings
output application/json

fun clearLine (line:String) = lower(line filter (isAlphanumeric ($)))
fun isPalindrome(line: String) = line == reverse(line)
---
lines(payload) filter (isPalindrome(clearLine($))) reduce ((line, acc=0) -> acc+sizeOf(line))
```

**Abhijeet Kumar** (Mar 7, 2023): 

```dataweave
%dw 2.0
import * from dw::core::Strings
output application/json
fun replaceSpecial(data:String) = (data replace  /[^a-z0-9A-Z]/ with "")
fun isPalindrome(data:String)=  if(lower(reverse(replaceSpecial(data))) == lower(replaceSpecial(data))) data else 0
---
sum(payload splitBy("\n") map ((item, index) -> if(isPalindrome(item) == 0 ) 0 else sizeOf(item)  ))
```

**Felix Schnabel** (Mar 7, 2023): My solution:

```dataweave
%dw 2.0
output application/json
import * from dw::core::Arrays
import * from dw::core::Strings
---
lines(payload)
filter(
    lower($ filter isAlphanumeric($)) then $ == $[-1 to 0]
)
map sizeOf($)
then sum($)
```

**Shyam Raj Prasad** (Mar 7, 2023): My approach for this.

```dataweave
%dw 2.0
import lines, isAlphanumeric, reverse from dw::core::Strings
output application/json
---
lines(payload) reduce ((item, accumulator = 0) ->
        do {
            var text = lower(item filter (isAlphanumeric($)))
            ---
            if(text == reverse(text)) sizeOf(item) + accumulator else accumulator + 0
        }
)
```

**Stefano Bernardini** (Mar 7, 2023): I can do better than this, but it works! 😅

```dataweave
%dw 2.0
output application/json

fun removeStuff(p: String) = upper(p) then $ replace  /[^a-z0-9A-Z ]/ with "" then $ replace " " with ""

fun isPalindrome(p: String) = removeStuff(p) == removeStuff(p[-1 to 0])

---
(
payload splitBy  "\n"
map (item, index) ->
{
   "paly" : isPalindrome(item),
   "size" : sizeOf(item)
})

filter $.paly

then sum($.size)
```

**prasadrnithin** (Mar 7, 2023): 

```dataweave
%dw 2.0
output application/json
import * from dw::core::Strings
var split_phrases = lines(payload)
var alpha_numeric_strings = split_phrases map ((item, index) -> item filter (isAlphanumeric($)))
---
sum(alpha_numeric_strings map ((item, index) -> if(lower(item) == lower(item)[-1 to 0]) sizeOf(split_phrases[index]) else 0))
```

---

## FAQs

### What does this DataWeave challenge ask you to do?

Each line of the input payload is treated as a separate phrase, and you review every phrase to find the ones that are palindromes, ignoring punctuation, spaces, and special characters. Then you retrieve the character count of the original phrase (including its punctuation, spaces, and special characters) and return the total sum of all the palindromes' character counts.

### How is the expected output of 148 calculated?

It is the sum of the character counts of every palindrome phrase in the input, counting each original phrase in full. For example `Anna` counts as 4, `2020/02/02` counts as 10, and `Mr. Owl ate my metal worm` counts as 25, and adding up the lengths of all the palindrome lines gives 148.

### Why is `Mr. Owl ate my metal worm` considered a palindrome here?

Because punctuation and spaces are not considered when checking for a palindrome, so once you strip them out it becomes `mrowlatemymetalworm`, which reads the same in reverse. The character count, though, is taken from the original phrase including its spaces and the period, which is 25.

### Which DataWeave functions does the sample solution use?

The solution imports `lines`, `isAlphanumeric`, `mapString`, and `reverse` from `dw::core::Strings`, then uses `lower` to normalize case, `mapString` with `isAlphanumeric` to drop non-alphanumeric characters, `reverse` to compare the cleaned line against itself, and `sizeOf` plus `sum` to count and total the characters.

### Do I need to use regex to remove the special characters?

No, the clues note that you do not need regex to find the alphanumeric characters because you can use `isAlphanumeric()` from the Strings module, and you can iterate through a string's characters with `mapString()` instead of `map()`.

### How can I replace `payload splitBy "\n"` in this challenge?

One of the clues points out that `payload splitBy "\n"` can be replaced with the `lines()` function from the Strings module, which splits the input payload into its individual phrases.