Skip to content

First concept exercise, 4th task: compare JSON objects without ordered keys #289

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions exercises/concept/shopping/bats-jq.bash
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,64 @@ jq() {
echo "$output"
return "$rc"
}

#############################################################
# These are extra assert functions for use in tests.

# Assert two JSON objects are equal.
# https://jqlang.org/manual/v1.7/#==-!=
#
# assert_objects_equal '{"a": 1, "b": 2}' '{"b":2,"a":1}'
# # => true
#
assert_objects_equal() {
local result=$(
jq -n --argjson actual "$1" \
--argjson expected "$2" \
'$actual == $expected'
)
[[ $result == "true" ]]
}

# Assert 2 floating-point values are "close enough".
#
# # are they the same to 2 decimal places?
# assert_float 1.993 1.995 # => true
#
# # are they the same to 3 decimal places?
# assert_float -d 3 1.993 1.995 # => false
#
assert_float() {
local OPTIND OPTARG
local decimals=2 actual expected
while getopts :d: opt; do
case $opt in
d) decimals=$OPTARG ;;
*) return 2 ;;
esac
done
shift $((OPTIND - 1))
# bash can't do floating point: use awk
read -r actual expected < <(
awk -v d="$decimals" -v a="$1" -v e="$2" '
BEGIN {
m = 10 ^ d
print int(a * m)/m, int(e * m)/m
}
'
)
# now call a bats-assert command to get the desired output
assert_equal "$actual" "$expected"
}

# Assert that an object's value of a given key is the expected value.
# This uses the bats `output` variable.
#
# run jq -f ...
# assert_key_value 'the_key' 'the_expected_value'
#
assert_key_value() {
local key=$1 expected=$2 actual
actual=$(jq -rc --arg key "$key" '.[$key]' <<< "$output")
assert_equal "$actual" "$expected"
}
8 changes: 7 additions & 1 deletion exercises/concept/shopping/test-shopping.bats
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,11 @@ load bats-jq
## task 4
run jq -c -f shopping.jq shopping-list.json
assert_success
assert_line --index 3 '{"buttermilk":"regular milk","melted butter":"vegetable oil","blueberries":"chopped apple"}'
assert_objects_equal \
"${lines[3]}" \
'{
"buttermilk":"regular milk",
"melted butter":"vegetable oil",
"blueberries":"chopped apple"
}'
}
62 changes: 61 additions & 1 deletion lib/bats-jq.bash
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
# - "Printing to the terminal", https://bats-core.readthedocs.io/en/stable/writing-tests.html#printing-to-the-terminal
# - "File descriptor 3", https://bats-core.readthedocs.io/en/stable/writing-tests.html#file-descriptor-3-read-this-if-bats-hangs


jq() {
local output stderr rc line
stderr=$(mktemp)
Expand All @@ -27,3 +26,64 @@ jq() {
echo "$output"
return "$rc"
}

#############################################################
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be updating all the exercises with the new bats-jq file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do that in a separate PR, to keep this one in focus

# These are extra assert functions for use in tests.

# Assert two JSON objects are equal.
# https://jqlang.org/manual/v1.7/#==-!=
#
# assert_objects_equal '{"a": 1, "b": 2}' '{"b":2,"a":1}'
# # => true
#
assert_objects_equal() {
local result=$(
jq -n --argjson actual "$1" \
--argjson expected "$2" \
'$actual == $expected'
)
[[ $result == "true" ]]
}

# Assert 2 floating-point values are "close enough".
#
# # are they the same to 2 decimal places?
# assert_float 1.993 1.995 # => true
#
# # are they the same to 3 decimal places?
# assert_float -d 3 1.993 1.995 # => false
#
assert_float() {
local OPTIND OPTARG
local decimals=2 actual expected
while getopts :d: opt; do
case $opt in
d) decimals=$OPTARG ;;
*) return 2 ;;
esac
done
shift $((OPTIND - 1))
# bash can't do floating point: use awk
read -r actual expected < <(
awk -v d="$decimals" -v a="$1" -v e="$2" '
BEGIN {
m = 10 ^ d
print int(a * m)/m, int(e * m)/m
}
'
)
# now call a bats-assert command to get the desired output
assert_equal "$actual" "$expected"
}

# Assert that an object's value of a given key is the expected value.
# This uses the bats `output` variable.
#
# run jq -f ...
# assert_key_value 'the_key' 'the_expected_value'
#
assert_key_value() {
local key=$1 expected=$2 actual
actual=$(jq -rc --arg key "$key" '.[$key]' <<< "$output")
assert_equal "$actual" "$expected"
}