'how to perform string operation in an input string and get the desired output

I have to write a shell script to accept the product order details as string input and display the total order amount and number of order under each category. INPUT: 101:Redmi:Mobile:15000#102:Samsung:TV:20000#103:OnePlus:Mobile:35000#104:HP:Laptop:65000#105:Samsung:Mobile:10000#106:Samsung:TV:30000

OUTPUT: Mobile:60000:3#TV:50000:2#Laptop:65000:1

I have to achieve this using sort,tr,cut,grep command only no sed awk should be used.



Solution 1:[1]

Here is my solution, it is improvable, I will explain it:

    x="101:Redmi:Mobile:15000#102:Samsung:TV:20000#103:OnePlus:Mobile:35000#104:HP:Laptop:65000#105:Samsung:Mobile:10000#106:Samsung:TV:30000"

echo $x > origin.txt

cat origin.txt | grep -E -o ':[a-Z]+:[0-9]+' | cut -c 2- > temp.txt

categories=()
quantity=()
items=()

while IFS= read -r line
do

    category=$(echo $line | grep -E -o '[a-Z]+')
    amount=($(( $(echo $line | grep -E -o '[0-9]+') )))

    if [ "0" = "${#categories[@]}" ]
    then
        # Add new element at the end of the array
        categories+=( $category )
        quantity+=( $amount )
        items+=(1)
        
    else
    
        let in=0
        let index=0
        let i=0
        # Iterate the loop to read and print each array element
        for value in "${categories[@]}"
        do
             if [ $category = $value ]
             then
                let in=$in+1
                let index=$i
             fi
             let i=$i+1
        done
        
        
        if [ $in = 0 ]
        then
            categories+=( $category )
            quantity+=( $amount )
            items+=(1)
        else
        
            let sum=$amount+${quantity[$index]}
            quantity[$index]=$sum
            
            let newitems=${items[$index]}+1
            items[$index]=$newitems
            
        fi
        
        
   fi

done < temp.txt

let j=0
for value in "${categories[@]}"
    do
        echo -n $value
        echo -n ':'
        echo -n ${quantity[$j]}
        echo -n ':'
        echo -n ${items[$j]}
        
        let k=$j+1
        if [ $k != ${#categories[@]} ]
        then
            echo -n '#'
        fi
        
        let j=$j+1
done

First i save the string in x and later in origin.txt, with cat, grep and cut I obtain the string in this format inside of temp.txt:

Mobile:15000 \n TV:20000 \n Mobile:35000 \n Laptop:65000 \n Mobile:10000 \n TV:30000

I create three arrays: categories for storage the name of the category, quantity for storage the amount of the category and items the objects of the category.

Later I read temp.txt line by line and using a simple regex I can get the name of the category and the amount of that line.

amount=($(( $(echo $line | grep -E -o '[0-9]+') )))

This is used to convert string into int.

The first if is for check if the array is empty, if it's empty we append the category, the amount and 1 object.

If is not empty we need to declare three variables:

  • in, for check if the incoming category is in the array or not
  • index, for storage the index of the existing category
  • i, for count the times we loop

If the category already exist in the array $in is setted to 1 or higher. If not $in still have the default value 0.

Next if in is equal to 0, means the category is new, we append to the three arrays.

If in is not equal to 0 using the index we can sum the new amount to the stored amount.

Finally when the main loop finish I print the data following the format you have mentioned.

The variable k is for not print the last "#", we compare k with the size of the array. Note that arrays start with 0 index.

Solution 2:[2]

The input is:

x="101:Redmi:Mobile:15000#102:Samsung:TV:20000#103:OnePlus:Mobile:35000#104:HP:Laptop:65000#105:Samsung:Mobile:10000#106:Samsung:TV:30000"

First, split records on lines:

tr '#' '\n' <<< "$x"

This gives:

101:Redmi:Mobile:15000
102:Samsung:TV:20000
103:OnePlus:Mobile:35000
104:HP:Laptop:65000
105:Samsung:Mobile:10000
106:Samsung:TV:30000

Better to keep only relevant data and store it in a variable:

data="$(tr '#' '\n' <<< "$x" | cut -d: -f3,4)"
echo "$data"

This gives:

Mobile:15000
TV:20000
Mobile:35000
Laptop:65000
Mobile:10000
TV:30000

Count how many items in each category. sed is used for formatting:

categories="$(cut -d: -f1 <<< "$data" | sort | uniq -c | sed 's/^ *//;s/ /:/')"
echo "$categories"

This gives:

1:Laptop
3:Mobile
2:TV

Then loop over categories to extract and compute total amount for each:

for i in $categories
do
  count=${i%:*}
  item=${i/*:}
  amount=$(($(grep $item <<< "$data"| cut -d: -f2 | tr '\n' '+')0))
  echo $item $amount $count
done

This gives:

Laptop 65000 1
Mobile 60000 3
TV 50000 2

Finally, a little bit of formatting after the done:

<...> done | tr ' \n' ':#' | sed 's/#$//' ; echo

This gives:

Laptop:65000:1#Mobile:60000:3#TV:50000:2

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 marc_s
Solution 2 mouviciel