From 28a5cee0ce97fde2d6c682fc353b5de2ba7c20bd Mon Sep 17 00:00:00 2001 From: seicho Date: Tue, 30 Jan 2024 14:37:23 +0900 Subject: [PATCH 1/5] first version --- 05.wc/wc.rb | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100755 05.wc/wc.rb diff --git a/05.wc/wc.rb b/05.wc/wc.rb new file mode 100755 index 0000000000..08518f598d --- /dev/null +++ b/05.wc/wc.rb @@ -0,0 +1,66 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'optparse' + +def main + opt = OptionParser.new + filter_params = {} + opt.on('-l') { |v| filter_params[:lines] = v } + opt.on('-w') { |v| filter_params[:chars] = v } + opt.on('-c') { |v| filter_params[:bytes] = v } + opt.parse! ARGV + filter_params = { lines: true, chars: true, bytes: true } if filter_params.empty? + formatted_stats = if ARGV.size == 1 + format_file_stats(file_stats:, filter_params:) + elsif ARGV.size >= 2 + file_stats_with_total = calcurate_total file_stats + format_file_stats(file_stats: file_stats_with_total, filter_params:) + else + input = readlines.join + stat = build_data(input) + format(stat:, filter_params:) + end + puts formatted_stats +end + +def file_stats + ARGV.map do |file_name| + contents = IO.readlines(file_name).join + stats = build_data(contents) + [file_name, { lines: stats[:lines], chars: stats[:chars], bytes: stats[:bytes] }] + end.to_h +end + +def build_data(contents) + lines = contents.scan(/(\n|\r)/).count + chars = contents.split(/\s+/).count + bytes = contents.size + { lines:, chars:, bytes: } +end + +def calcurate_total(file_stats) + stats = file_stats.values + total = { + total: stats[0].merge(*stats[1..]) { |_, old_value, new_value| old_value + new_value } + } + file_stats.merge(total) +end + +def format_file_stats(file_stats:, filter_params:) + col_width = max_col_width(file_stats) + file_stats.map { |name, stat| format(stat:, filter_params:, col_width:) + " #{name}" }.join("\n") +end + +def max_col_width(file_stats) + file_stats.values.map do |stat| + stat.values.max.to_s.size + end.max +end + +def format(stat:, filter_params:, col_width: 7) + filtered_stat = stat.filter { |k| filter_params.keys[0..].include?(k) }.values + filtered_stat.map { |v| v.to_s.rjust(col_width) }.join(' ') +end + +__FILE__ == $PROGRAM_NAME && main From 5daa78fadfaa340bf2821445a44c2083907a27a0 Mon Sep 17 00:00:00 2001 From: seicho Date: Fri, 9 Feb 2024 10:17:56 +0900 Subject: [PATCH 2/5] Define uniform methods for stdin and arguments --- 05.wc/wc.rb | 64 ++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/05.wc/wc.rb b/05.wc/wc.rb index 08518f598d..40dcddc60f 100755 --- a/05.wc/wc.rb +++ b/05.wc/wc.rb @@ -7,39 +7,41 @@ def main opt = OptionParser.new filter_params = {} opt.on('-l') { |v| filter_params[:lines] = v } - opt.on('-w') { |v| filter_params[:chars] = v } + opt.on('-w') { |v| filter_params[:words] = v } opt.on('-c') { |v| filter_params[:bytes] = v } opt.parse! ARGV - filter_params = { lines: true, chars: true, bytes: true } if filter_params.empty? - formatted_stats = if ARGV.size == 1 - format_file_stats(file_stats:, filter_params:) - elsif ARGV.size >= 2 - file_stats_with_total = calcurate_total file_stats - format_file_stats(file_stats: file_stats_with_total, filter_params:) - else - input = readlines.join - stat = build_data(input) - format(stat:, filter_params:) - end - puts formatted_stats -end + filter_params = { lines: true, words: true, bytes: true } if filter_params.empty? -def file_stats - ARGV.map do |file_name| - contents = IO.readlines(file_name).join - stats = build_data(contents) - [file_name, { lines: stats[:lines], chars: stats[:chars], bytes: stats[:bytes] }] - end.to_h + # contents_with_source_name = ARGV.empty? ? {stdin: readlines.join} : ARGV.map{ |file_name| [file_name, IO.readlines(file_name, nil).pop] }.to_h + if ARGV.empty? + contents = [readlines.join] + source_names = [:stdin] + else + contents = ARGV.map{ |file_name| IO.readlines(file_name, nil).pop } + source_names = ARGV + end + contents_with_source_name = source_names.zip(contents).to_h + stats = calcurate_stats(contents_with_source_name) + col_width = stats.has_key?(:stdin) ? 7 : max_col_width(stats) + formatted_stats = format_stats(stats:, filter_params:, col_width:) + puts formatted_stats end def build_data(contents) lines = contents.scan(/(\n|\r)/).count - chars = contents.split(/\s+/).count - bytes = contents.size - { lines:, chars:, bytes: } + words = contents.split(/[\s^ ]+/).count + bytes = contents.bytesize + { lines:, words:, bytes: } +end + +def calcurate_stats(contents) + stats = contents.map{ |source_name, contents| [source_name, build_data(contents)] }.to_h + return stats if stats.length == 1 + + add_total(stats) end -def calcurate_total(file_stats) +def add_total(file_stats) stats = file_stats.values total = { total: stats[0].merge(*stats[1..]) { |_, old_value, new_value| old_value + new_value } @@ -47,9 +49,12 @@ def calcurate_total(file_stats) file_stats.merge(total) end -def format_file_stats(file_stats:, filter_params:) - col_width = max_col_width(file_stats) - file_stats.map { |name, stat| format(stat:, filter_params:, col_width:) + " #{name}" }.join("\n") +def format_stats(stats:, filter_params:, col_width:) + stats.map do |name, stat| + filtered_stat_values = stat.filter { |k| filter_params.keys[0..].include?(k) }.values + formatted_stat_values = filtered_stat_values.map { |v| v.to_s.rjust(col_width) }.join(' ') + name == :stdin ? formatted_stat_values : "#{formatted_stat_values} #{name}" + end.join("\n") end def max_col_width(file_stats) @@ -58,9 +63,4 @@ def max_col_width(file_stats) end.max end -def format(stat:, filter_params:, col_width: 7) - filtered_stat = stat.filter { |k| filter_params.keys[0..].include?(k) }.values - filtered_stat.map { |v| v.to_s.rjust(col_width) }.join(' ') -end - __FILE__ == $PROGRAM_NAME && main From 790b32d8d5f1a7ad795f24e5836bc7d244f8202d Mon Sep 17 00:00:00 2001 From: seicho Date: Fri, 9 Feb 2024 10:29:18 +0900 Subject: [PATCH 3/5] Remove useless comment and change order of methods for readability --- 05.wc/wc.rb | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/05.wc/wc.rb b/05.wc/wc.rb index 40dcddc60f..13660e9b5f 100755 --- a/05.wc/wc.rb +++ b/05.wc/wc.rb @@ -12,7 +12,6 @@ def main opt.parse! ARGV filter_params = { lines: true, words: true, bytes: true } if filter_params.empty? - # contents_with_source_name = ARGV.empty? ? {stdin: readlines.join} : ARGV.map{ |file_name| [file_name, IO.readlines(file_name, nil).pop] }.to_h if ARGV.empty? contents = [readlines.join] source_names = [:stdin] @@ -27,6 +26,13 @@ def main puts formatted_stats end +def calcurate_stats(contents_with_source_name) + stats = contents_with_source_name.map{ |source_name, contents| [source_name, build_data(contents)] }.to_h + return stats if stats.length == 1 + + add_total(stats) +end + def build_data(contents) lines = contents.scan(/(\n|\r)/).count words = contents.split(/[\s^ ]+/).count @@ -34,13 +40,6 @@ def build_data(contents) { lines:, words:, bytes: } end -def calcurate_stats(contents) - stats = contents.map{ |source_name, contents| [source_name, build_data(contents)] }.to_h - return stats if stats.length == 1 - - add_total(stats) -end - def add_total(file_stats) stats = file_stats.values total = { From 475c58f1d43d9047a106793ba4686aacc51820d2 Mon Sep 17 00:00:00 2001 From: seicho Date: Tue, 13 Feb 2024 13:08:11 +0900 Subject: [PATCH 4/5] Change data structure for simplificatoin. --- 05.wc/wc.rb | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/05.wc/wc.rb b/05.wc/wc.rb index 13660e9b5f..1080c096b4 100755 --- a/05.wc/wc.rb +++ b/05.wc/wc.rb @@ -3,6 +3,8 @@ require 'optparse' +STDIN_COL_WIDTH = 7 + def main opt = OptionParser.new filter_params = {} @@ -12,54 +14,45 @@ def main opt.parse! ARGV filter_params = { lines: true, words: true, bytes: true } if filter_params.empty? - if ARGV.empty? - contents = [readlines.join] - source_names = [:stdin] - else - contents = ARGV.map{ |file_name| IO.readlines(file_name, nil).pop } - source_names = ARGV - end - contents_with_source_name = source_names.zip(contents).to_h - stats = calcurate_stats(contents_with_source_name) - col_width = stats.has_key?(:stdin) ? 7 : max_col_width(stats) + contents_with_filename = ARGV.empty? ? [readlines.join] : ARGV.map { |file_name| [File.read(file_name), file_name] } + stats = calcurate_stats(contents_with_filename) + col_width = stats.first[:file_name].nil? ? STDIN_COL_WIDTH : max_col_width(stats) formatted_stats = format_stats(stats:, filter_params:, col_width:) puts formatted_stats end -def calcurate_stats(contents_with_source_name) - stats = contents_with_source_name.map{ |source_name, contents| [source_name, build_data(contents)] }.to_h +def calcurate_stats(contents_with_filename) + stats = contents_with_filename.map { |contents, file_name| build_data(contents, file_name) } return stats if stats.length == 1 add_total(stats) end -def build_data(contents) +def build_data(contents, file_name) lines = contents.scan(/(\n|\r)/).count words = contents.split(/[\s^ ]+/).count bytes = contents.bytesize - { lines:, words:, bytes: } + { lines:, words:, bytes:, file_name: } end def add_total(file_stats) - stats = file_stats.values - total = { - total: stats[0].merge(*stats[1..]) { |_, old_value, new_value| old_value + new_value } - } - file_stats.merge(total) + total = file_stats[0].merge(*file_stats[1..]) { |_, old_value, new_value| old_value + new_value } + total[:file_name] = :total + file_stats << total end def format_stats(stats:, filter_params:, col_width:) - stats.map do |name, stat| + stats.map do |stat| filtered_stat_values = stat.filter { |k| filter_params.keys[0..].include?(k) }.values formatted_stat_values = filtered_stat_values.map { |v| v.to_s.rjust(col_width) }.join(' ') - name == :stdin ? formatted_stat_values : "#{formatted_stat_values} #{name}" + [formatted_stat_values, stat[:file_name]].compact.join(' ') end.join("\n") end def max_col_width(file_stats) - file_stats.values.map do |stat| - stat.values.max.to_s.size + file_stats.map do |stat| + stat.reject { |key| key == :file_name }.values.max.to_s.size end.max end -__FILE__ == $PROGRAM_NAME && main +main if __FILE__ == $PROGRAM_NAME From f63d0626c10c9b08491569807194218311888c3b Mon Sep 17 00:00:00 2001 From: seicho Date: Thu, 15 Feb 2024 13:55:20 +0900 Subject: [PATCH 5/5] Fix bwrong line count bug. --- 05.wc/wc.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/05.wc/wc.rb b/05.wc/wc.rb index 1080c096b4..0f5031b972 100755 --- a/05.wc/wc.rb +++ b/05.wc/wc.rb @@ -15,13 +15,13 @@ def main filter_params = { lines: true, words: true, bytes: true } if filter_params.empty? contents_with_filename = ARGV.empty? ? [readlines.join] : ARGV.map { |file_name| [File.read(file_name), file_name] } - stats = calcurate_stats(contents_with_filename) + stats = calculate_stats(contents_with_filename) col_width = stats.first[:file_name].nil? ? STDIN_COL_WIDTH : max_col_width(stats) formatted_stats = format_stats(stats:, filter_params:, col_width:) puts formatted_stats end -def calcurate_stats(contents_with_filename) +def calculate_stats(contents_with_filename) stats = contents_with_filename.map { |contents, file_name| build_data(contents, file_name) } return stats if stats.length == 1 @@ -30,7 +30,7 @@ def calcurate_stats(contents_with_filename) def build_data(contents, file_name) lines = contents.scan(/(\n|\r)/).count - words = contents.split(/[\s^ ]+/).count + words = contents.split(/[^\s ][\s ]+/).count bytes = contents.bytesize { lines:, words:, bytes:, file_name: } end