'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 |
