Skip to content
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

ledger #325

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open

ledger #325

wants to merge 12 commits into from

Conversation

Falilah
Copy link
Contributor

@Falilah Falilah commented Dec 24, 2024

Closes #196

Opened instead of #316

I have not done the switches you recommended in the previous one and reason is that I am still thinking of how best to write the sorting logic of the ByteArray.

Copy link

Hello 👋 Thanks for your PR.

This repo does not currently have dedicated maintainers. Our cross-track maintainers team will attempt to review and merge your PR, but it will likely take longer for your PR to be reviewed.

If you enjoy contributing to Exercism and have a track-record of doing so successfully, you might like to become an Exercism maintainer for this track.

Please feel free to ask any questions, or chat to us about anything to do with this PR or the reviewing process on the Exercism forum.

(cc @exercism/cross-track-maintainers)

@0xNeshi 0xNeshi added x:module/practice-exercise Work on Practice Exercises x:type/content Work on content (e.g. exercises, concepts) x:rep/large Large amount of reputation x:action/create Work on something from scratch x:type/coding Write code that is not student-facing content (e.g. test-runners, generators, but not exercises) labels Dec 25, 2024
Copy link
Contributor

@0xNeshi 0xNeshi left a comment

Choose a reason for hiding this comment

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

Looks much better

};

let formatted_value = format_number(int_value, negative, currency, locale);
let mut extra = "";
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
let mut extra = "";
let mut padding = "";

more accurate

i += 1;
}

const AMOUNT_COLUMN_WIDTH: usize = 13;
Copy link
Contributor

Choose a reason for hiding this comment

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

Move all constants outside to the top of the file, and outside any functions

Comment on lines +121 to +139
fn format_amount(amount_in_cents: @i32, currency: @Currency, locale: @Locale) -> ByteArray {
let amount_in_cents = format!("{amount_in_cents}");
let mut int_value: u32 = 0;
let mut negative = false;
let mut i = 0;

if amount_in_cents[i] == '-' {
negative = true;
i += 1;
}

const AMOUNT_COLUMN_WIDTH: usize = 13;
while i < amount_in_cents.len() {
let zero_ascii = '0';
if let Option::Some(digit) = Option::Some(amount_in_cents[i] - zero_ascii) {
int_value = int_value * 10 + digit.into();
}
i += 1;
};
Copy link
Contributor

@0xNeshi 0xNeshi Dec 25, 2024

Choose a reason for hiding this comment

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

This is unnecessary logic - the first line turns an integer amount_in_cents value into a bytearray, and then immediately after in this while loop, it parses that same bytearray back into an integer.

Refactor this to utilize the original integer value directly.

You can even move negative into the formatter, thus removing the extra function parameter, and doing away with the local variable

Comment on lines +185 to +190
if fraction < 10 {
result.append_byte('0');
fraction.append_formatted_to_byte_array(ref result, 10);
} else {
fraction.append_formatted_to_byte_array(ref result, 10);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if fraction < 10 {
result.append_byte('0');
fraction.append_formatted_to_byte_array(ref result, 10);
} else {
fraction.append_formatted_to_byte_array(ref result, 10);
}
if fraction < 10 {
result.append_byte('0');
}
fraction.append_formatted_to_byte_array(ref result, 10);

let whole = value / 100;

result += add_sep(whole, locale);
let fraction = value % 100;
Copy link
Contributor

Choose a reason for hiding this comment

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

move fraction closer to where it's actually used

Comment on lines +84 to +93
Locale::en_US => {
day += "/";
month += "/";
ByteArrayTrait::concat(@month, @ByteArrayTrait::concat(@day, @year))
},
Locale::nl_NL => {
day += "-";
month += "-";
ByteArrayTrait::concat(@day, @ByteArrayTrait::concat(@month, @year))
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not use the shorter format!?

Comment on lines +98 to +119
fn split_date(date: @ByteArray) -> (ByteArray, ByteArray, ByteArray) {
let mut year = "";
let mut month = "";
let mut day = "";
let mut sep = 0;
let mut i = 0;

while i < date.len() {
if sep == 0 && i < 4 && date[i] != '-' {
year.append_byte(date[i]);
} else if date[i] == '-' {
sep += 1;
} else if sep == 1 && i < 7 && date[i] != '-' {
month.append_byte(date[i]);
} else {
day.append_byte(date[i]);
}
i += 1;
};

(year, month, day)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Here's a more efficient solution:

Suggested change
fn split_date(date: @ByteArray) -> (ByteArray, ByteArray, ByteArray) {
let mut year = "";
let mut month = "";
let mut day = "";
let mut sep = 0;
let mut i = 0;
while i < date.len() {
if sep == 0 && i < 4 && date[i] != '-' {
year.append_byte(date[i]);
} else if date[i] == '-' {
sep += 1;
} else if sep == 1 && i < 7 && date[i] != '-' {
month.append_byte(date[i]);
} else {
day.append_byte(date[i]);
}
i += 1;
};
(year, month, day)
}
fn split_date(date: ByteArray) -> (ByteArray, ByteArray, ByteArray) {
// Helper to append bytes to a ByteArray
#[cairofmt::skip]
let append_range = |start: usize, end: usize| -> ByteArray {
let mut part = "";
for i in start..end {
part.append_byte(date[i]);
};
part
};
// Find separator positions
let mut separators = array![];
for i in 0..date.len() {
if date[i] == '-' {
separators.append(i);
}
};
// Extract year, month, and day
let year = append_range(0, *separators[0]);
let month = append_range(*separators[0] + 1, *separators[1]);
let day = append_range(*separators[1] + 1, date.len());
(year, month, day)
}

Benefits:

  • Avoids multiple checks during iteration.
  • The logic is broken into meaningful parts, making the code easier to test and reuse.
  • The intent of each section is clear and self-contained



fn add_sep(whole: u32, locale: @Locale) -> ByteArray {
let mut result = "";
Copy link
Contributor

Choose a reason for hiding this comment

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

redundant result, you can remove it and return values directly

Comment on lines +204 to +205
let mut temp = "";
@whole.append_formatted_to_byte_array(ref temp, 10);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
let mut temp = "";
@whole.append_formatted_to_byte_array(ref temp, 10);
let temp = format!("{whole}");

no need to overcomplicate things

Comment on lines +41 to +42
Entry { date: "2015-01-01", description: "Buy present", amount_in_cents: -1000 },
Entry { date: "2015-01-02", description: "Get present", amount_in_cents: 1000 },
Copy link
Contributor

Choose a reason for hiding this comment

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

Double check that all test entries are in correct order

These two are not

let entries = array![
Entry { date: "2015-01-01", description: "Something", amount_in_cents: -1 },
Entry { date: "2015-01-01", description: "Something", amount_in_cents: 0 },
Entry { date: "2015-01-01", description: "Something", amount_in_cents: 1 },
Copy link
Contributor

Choose a reason for hiding this comment

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

exercises/practice/ledger/tests/ledger.cairo Show resolved Hide resolved
let locale = Locale::en_US;
let entries = array![
Entry { date: "2015-01-01", description: "Buy present", amount_in_cents: -1000 },
Entry { date: "2015-01-01", description: "Get present", amount_in_cents: 1000 },
Copy link
Contributor

Choose a reason for hiding this comment

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

config.json Outdated Show resolved Hide resolved
}

pub fn format_entries(
currency: Currency, locale: Locale, entries: Array<Entry>,
Copy link
Contributor

@0xNeshi 0xNeshi Dec 26, 2024

Choose a reason for hiding this comment

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

My eyes hurt looking at this implementation, which means you did a great job! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
x:action/create Work on something from scratch x:module/practice-exercise Work on Practice Exercises x:rep/large Large amount of reputation x:type/coding Write code that is not student-facing content (e.g. test-runners, generators, but not exercises) x:type/content Work on content (e.g. exercises, concepts)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Practice Exercise: ledger
2 participants