I'm trying to compile this procedure in Informix Data Base, but the result is:
Invalid default value for column/variable (ed_fec_sol).
CREATE PROCEDURE admfte.cds93_val_deu_sol ( ec_tip_deu CHAR(02),
ec_ind_ori_com CHAR,
ec_num_ruc LIKE sol_comp.num_ruc_des,
ec_num_val LIKE sol_comp.num_doc_des,
ec_per_tri LIKE sol_comp.per_tri_des,
ec_sem_doc LIKE sol_comp.sem_doc_des,
ec_cod_tri LIKE sol_comp.cod_tri_des,
ec_cod_dep CHAR(4),
ed_fec_sol DATE DEFAULT '01/01/0001' )
RETURNING SMALLINT, CHAR(250), CHAR(8), SMALLINT, CHAR(17),
CHAR(6), CHAR(6), CHAR(6), CHAR(6), CHAR, DATE, DATE;
{*************************************************************************}
DEFINE ls_cod_for LIKE crt.crt_formul;
DEFINE lc_num_doc LIKE dbt.dbt_numdoc;
DEFINE lc_cod_tri LIKE valores.cod_tri;
DEFINE lc_cod_tri_aso LIKE valores.cod_tri_aso;
DEFINE lc_cod_tip_doc LIKE valores.cod_tip_doc;
DEFINE lc_not_abo CHAR(8);
DEFINE ld_fec_ori DATE;
DEFINE ld_fec_pre DATE;
DEFINE lc_per_doc CHAR(6);
DEFINE lc_sem_doc CHAR;
DEFINE ls_cod_err SMALLINT;
DEFINE lc_msg_err CHAR(250);
DEFINE li_count INTEGER;
DEFINE lc_num_exp_fra LIKE pre_op_fra.num_doc;
DEFINE GLOBAL gc_CodUsu CHAR(08) DEFAULT USER;DEFINE GLOBAL gc_FlgDeb CHAR(01) DEFAULT '0';DEFINE lc_file_log CHAR(100); DEFINE lc_flg_debug CHAR(1); DEFINE lc_flg_explain CHAR(1); DEFINE lc_MsgDebIni VARCHAR(60); DEFINE lc_MsgDebFin VARCHAR(60);
SET OPTIMIZATION LOW;
IF gc_FlgDeb = '1' THEN SELECT flg_debug, flg_explain INTO lc_flg_debug, lc_flg_explain FROM pbdebugspl WHERE usuario = USER AND nom_spl = "cds93_val_deu_sol"; IF lc_flg_debug = "1" THEN LET lc_file_log = "/tmp/" || USER || "_" || "cds93_val_deu_sol" || ".log"; LET lc_MsgDebIni = "Iniciando ===> " || "cds93_val_deu_sol"; LET lc_MsgDebFin = "Finalizando ===> " || "cds93_val_deu_sol"; SET DEBUG FILE TO lc_file_log; TRACE ON; TRACE lc_MsgDebIni; END IF; IF lc_flg_explain = "1" THEN SET EXPLAIN ON; END IF; ELSE LET lc_flg_debug = '0'; END IF;
LET lc_not_abo = "0";
LET ld_fec_ori = '01/01/0001';
LET lc_per_doc = ec_per_tri;
LET lc_sem_doc = ec_sem_doc;
LET lc_cod_tri = ec_cod_tri;
LET lc_cod_tri_aso = "0";
LET lc_cod_tip_doc = "0";
IF ec_tip_deu = "02" THEN -- DDJJ
CALL cds93_ver_hay_ops (ec_num_ruc, ec_per_tri, ec_sem_doc, ec_cod_tri)
RETURNING ls_cod_err, lc_msg_err;
IF ls_cod_err > 0 THEN -- ls cod_err contiene la cantidad de OPs
RETURN -24, lc_msg_err, "0", 0, "0", "0", "0", "0",
'000101', '0',
'01/01/0001', '01/01/0001';
END IF
CALL cds91_existe_deb (ec_num_ruc, ec_per_tri, ec_sem_doc, ec_cod_tri, ec_cod_dep)
RETURNING ls_cod_err, lc_msg_err, lc_not_abo,ls_cod_for,lc_num_doc,ld_fec_pre;
IF ls_cod_err = 1 THEN
CALL cds91_fecha_deb (ec_num_ruc, ec_per_tri, ec_sem_doc,
ec_cod_tri, ec_cod_dep, ed_fec_sol)
RETURNING ls_cod_err, lc_msg_err, lc_not_abo, ls_cod_for,
lc_num_doc, ld_fec_ori, ld_fec_pre;
LET lc_per_doc = ec_per_tri;
LET lc_sem_doc = ec_sem_doc;
LET lc_cod_tri = ec_cod_tri;
LET lc_cod_tri_aso = '0';
LET lc_cod_tip_doc = '0';
--[2008-000422] : INICIO. SAU20082I020401360]
ELSE
RETURN ls_cod_err, lc_msg_err, "0", 0, "0", "0", "0", "0", '000101',
'0', '01/01/0001', '01/01/0001';
END IF
--[2008-000422] : FIN. SAU20082I020401360]
ELIF ec_tip_deu = "01" THEN -- VALOR
CALL cds93_obt_dat_val (ec_num_val, ec_num_ruc, ec_ind_ori_com)
RETURNING ls_cod_err, lc_msg_err, lc_cod_tri, lc_cod_tri_aso,
lc_cod_tip_doc, lc_per_doc, lc_sem_doc, ld_fec_ori, ld_fec_pre;
IF ls_cod_err < 0 THEN
RETURN ls_cod_err, lc_msg_err, "0", 0, "0", "0", "0", "0", '000101',
'0', '01/01/0001', '01/01/0001';
END IF
LET ls_cod_for = 0;
LET lc_num_doc = ec_num_val;
{PAS20082A510000808 : INICIO}
IF lc_cod_tip_doc = '001004' OR
lc_cod_tip_doc = '001005' OR
lc_cod_tip_doc = '001007' OR
lc_cod_tip_doc = '017004' OR
lc_cod_tip_doc = '017504' OR
lc_cod_tip_doc = '017604' THEN
IF (lc_cod_tip_doc = '001004' OR
lc_cod_tip_doc = '001005' OR
lc_cod_tip_doc = '001007') THEN
SELECT MAX(num_doc)
INTO lc_num_exp_fra
FROM pre_op_fra
WHERE pre_op_fra.num_val = ec_num_val
AND ind_est_op = '2'
AND ind_gen_op = '3';
IF lc_num_exp_fra IS NOT NULL AND
LENGTH(lc_num_exp_fra) > 0 THEN
SELECT COUNT(*)
INTO li_count
FROM pre_op_fra, valores
WHERE pre_op_fra.num_val = valores.num_val
AND pre_op_fra.num_doc = lc_num_exp_fra
AND ind_est_op = '2'
AND ind_gen_op IN ( '1',
'2' )
AND ind_sal = '1';
IF li_count = 0 THEN
RETURN ls_cod_err, lc_msg_err, lc_not_abo, ls_cod_for, lc_num_doc, lc_cod_tri,
lc_cod_tri_aso, lc_cod_tip_doc, lc_per_doc, lc_sem_doc, ld_fec_ori,
ld_fec_pre;
END IF
END IF
ELSE
RETURN ls_cod_err, lc_msg_err, lc_not_abo, ls_cod_for, lc_num_doc, lc_cod_tri,
lc_cod_tri_aso, lc_cod_tip_doc, lc_per_doc, lc_sem_doc, ld_fec_ori,
ld_fec_pre;
END IF
END IF
{PAS20082A510000808 : FIN}
END IF
{** Existe restriccion para la compensacion a determiandos tributos **}
CALL cds93_val_tributo(lc_cod_tri, 1)
RETURNING ls_cod_err, lc_msg_err;
IF ls_cod_err < 1 THEN
RETURN ls_cod_err, lc_msg_err, "0", 0, "0", "0", "0", "0", '000101',
'0', '01/01/0001', '01/01/0001';
END IF
RETURN ls_cod_err, lc_msg_err, lc_not_abo, ls_cod_for, lc_num_doc, lc_cod_tri,
lc_cod_tri_aso, lc_cod_tip_doc, lc_per_doc, lc_sem_doc, ld_fec_ori,
ld_fec_pre;
END PROCEDURE
How can I fix this?
As Jonathan mentioned, the key to your problem is the DBDATE environment variable, although other environment variables can be used (GL_DATE, CLIENT_LOCALE) this one takes precedent.
You can check if you have any of this set by querying the sysenvses SMI table:
SELECT envses_name, envses_value
FROM sysmaster:sysenvses
WHERE envses_name IN (
'DBDATE',
'GL_DATE',
'CLIENT_LOCALE'
)
AND envses_sid = DBINFO('sessionid')
ORDER BY DECODE(envses_name,
'DBDATE', 0,
'GL_DATE', 1,
'CLIENT_LOCALE', 2
);
This will get you by the order of precedence.
If you have not set any of this you should check if DBDATE is set on the server side by querying sysenv:
SELECT env_name, env_value
FROM sysmaster:sysenv
WHERE env_name = 'DBDATE';
If in doubt you can just check how dates are dealt by:
SELECT CURRENT::DATE
FROM sysmaster:sysdual;
Related
If I have a table like this, how would I print all the values?
local Buyers = {
{[Name] = "Birk", [SecName] = "Birk2nd", [ThirdName] = "Birk3nd"},
{[Name] = "Bob", [SecName] = "Bob2nd", [ThirdName] = "Bob3nd"},
}
It should end up printing:
First Name: Birk
Second Name: Birk2nd
Third Name: Birk3nd
FirstName: Bob
Second Name: Bob2nd
Third Name: Bob3nd
what i can think of
local Buyers = {
{["Name"] = "Birk", ["SecName"] = "Birk2nd", ["ThirdName"] = "Birk3nd"},
{["Name"] = "Bob", ["SecName"] = "Bob2nd", ["ThirdName"] = "Bob3nd"},
}
for _, person in pairs(Buyers) do
print("First name: "..person.Name)
print("Second name: "..person.SecName)
print("Third name: "..person.ThirdName)
print()
end
For your case, like this:
local function serialise_buyer (buyer)
return ('First Name: ' .. (buyer.Name or '')) .. '\n'
.. ('Second Name: ' .. (buyer.SecName or '')) .. '\n'
.. ('Third Name: ' .. (buyer.ThirdName or '')) .. '\n'
end
local Buyers = {
{Name = "Birk", SecName = "Birk2nd", ThirdName = "Birk3rd"},
{Name = "Bob", SecName = "Bob2nd", ThirdName = "Bob3rd"},
}
for _, buyer in ipairs (Buyers) do
print (serialise_buyer (buyer))
end
A more generic solution, with sorting:
local sort, rep, concat = table.sort, string.rep, table.concat
local function serialise (var, sorted, indent)
if type (var) == 'string' then
return "'" .. var .. "'"
elseif type (var) == 'table' then
local keys = {}
for key, _ in pairs (var) do
keys[#keys + 1] = key
end
if sorted then
sort (keys, function (a, b)
if type (a) == type (b) and (type (a) == 'number' or type (a) == 'string') then
return a < b
elseif type (a) == 'number' and type (b) ~= 'number' then
return true
else
return false
end
end)
end
local strings = {}
local indent = indent or 0
for _, key in ipairs (keys) do
strings [#strings + 1]
= rep ('\t', indent + 1)
.. serialise (key, sorted, indent + 1)
.. ' = '
.. serialise (var [key], sorted, indent + 1)
end
return 'table (\n' .. concat (strings, '\n') .. '\n' .. rep ('\t', indent) .. ')'
else
return tostring (var)
end
end
local Buyers = {
{Name = "Birk", SecName = "Birk2nd", ThirdName = "Birk3rd"},
{Name = "Bob", SecName = "Bob2nd", ThirdName = "Bob3rd"},
[function () end] = 'func',
[{'b', 'd'}] = {'e', 'f'}
}
print (serialise (Buyers, true))
The answer I found is with one level of struct. In my code I want to find out how many same dates is in inside of the struct of the struct. I never use equatable before so I'm not sure how to implement it with struct inside struct.
I'm using Charts pod and the array chartMetricItemSets contains different readings with multiples days. ex: ["name": sugar, [["date": "Oct 10", "value": 120], ["date": "Oct 10", "value": 121], ["date": "Oct 11", "value": 120], ["date": "Oct 13", "value": 120], ["date": "Oct 13", "value": 121]]. The xAxisDates will be ["Oct 10", "Oct 11", "Oct 13"]
the x is the date and the y is for the same date sums
Goal: chartDataEntries should append [x:0(10/10), y:241], [x:1(10/11), y:120], [x:2(10/13), y: 241]
Thank you!
struct ChartData {
let name: String
let value: [ChartValue]
}
struct ChartValue {
let date: Date
let value: Double
}
var chartMetricItemSets = [[ChartData]]()
var xAxisDates = [String]() //date array
func getChartDataSets() {
for (index, chartHealthItemSet) in chartMetricItemSets.enumerated() {
let dataSourceSelected = dataSourcesSelected[index]
var chartDataEntries = [ChartDataEntry]() //array display on graph
var chartYaxis: Double = 0
var datePoint: Int = 0
var countChartYaxis = 0
var duplicateDateCount = 0
for (index, item) in chartHealthItemSet[0].value.enumerated() {
for (dateIndex, date) in xAxisDates.enumerated() {
if item.date.convertDateToString(dateFormat: .monthDay) == date {
chartYaxis += item.value
datePoint = dateIndex
chartYaxis += 1
break
}
//missing counting duplicateDateCount
if duplicateDateCount == countChartYaxis {
chartDataEntries.append(ChartDataEntry(x: Double(datePoint), y: chartYaxis.roundUpNDecimal(n: 2)))
}
}
print("chartYaxis: \(chartYaxis)")
print("chartDataEntries: \(chartDataEntries)")
}
chartDataEntries.sort(by: { $0.x < $1.x })
let set = LineChartDataSet(entries: chartDataEntries,
label: HEALTH_TABLE.allCases[dataSourceSelected].info.name)
set.mode = .linear // cubic line effect (smoother)
set.colors = [HEALTH_TABLE.allCases[dataSourceSelected].info.chartColor]
set.setCircleColor(.black)
set.lineWidth = 1.5
set.circleRadius = 3
set.drawCircleHoleEnabled = false
set.drawCirclesEnabled = true
set.drawHorizontalHighlightIndicatorEnabled = true
lineChartData?.addDataSet(set)
}
chartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: xAxisDates)
chartView.xAxis.granularity = 1
chartView.xAxis.axisMinLabels = 4
chartView.xAxis.axisMaxLabels = 6
DispatchQueue.main.async {
self.chartView.data = self.lineChartData
}
}
In Swift 4 a useful API was introduced to group arrays to dictionaries by an arbitrary predicate.
You can group chartHealthItemSet[0]
let grouped = Dictionary(grouping: chartHealthItemSet[0], by: {$0.date.convertDateToString(dateFormat: .monthDay)}
The result is ["10/10":[ChartData], "10/11":[ChartData], ...]
Sort the keys and assign them to xAxisDates
xAxisDates = grouped.keys.sorted()
To get the sum of the values get the array from the grouped dictionary for the date key, map it to the values and sum them up
for (index, date) in xAxisDates.enumerated() {
let sum = grouped[date]!.map{$0.value}.reduce{0.0, +}
chartDataEntries.append(ChartDataEntry(x: Double(index), y: sum.roundUpNDecimal(n: 2)))
}
I need to append items to an array using a specific algorithm:
with start index == 2 and separator == 5
for example input array:
["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"]
for example output array:
["1", "1", "X", "1", "1", "1", "1", "1", "X", "1", "1", "1", "1", "1", "X", "1", "1", "1", "1"]
here is my code, but I can not find a way to make it work properly, looking forward to some help
var mixStartIndex = 2
var mixSeparator = 5
let array = ["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"]
var result = [AnyObject]()
for (index, item) in array.enumerated() {
if (index > mixStartIndex+mixSeparator && index % mixSeparator == 0) || index == mixStartIndex {
result.append("X" as AnyObject)
}
result.append(item as AnyObject)
}
print(result)
PS I am working with objects, just made example with strings for simplicity
You are missing to append the input array as a part of the output array
Try adding this
else
{
result.append(array[index])
}
Update :
changing the condition to if (((index-mixStartIndex)%mixSeparator)==0 || index == mixStartIndex) works for the fifth index
var mixStartIndex = 2
var mixSeparator = 6
func mix(array: [String]) -> [String] {
guard !array.isEmpty else { return array }
var result = [String]()
for (index, _) in array.enumerated() {
if (((index-mixStartIndex)%mixSeparator)==0 || index == mixStartIndex) {
result.append("X")
}
else
{
result.append(array[index])
}
}
return result
}
This works and is simpler than the accepted answer:
extension Array {
func example(start: Index, seperator: Index, insert: Element) -> [Element] {
var result = self
var index = start
while index < result.count {
result.insert(insert, at: index)
index += seperator + 1
}
return result
}
// a functional solution
func example2(start: Index, seperator: Index, insert: Element) -> [Element] {
return self.enumerated().flatMap { offset, element in
offset == start || offset > start && (offset - start) % seperator == 0 ? [insert, element] : [element]
}
}
}
print(array.example(start: mixStartIndex, seperator: mixSeparator, insert: "X"))
print(array.example2(start: mixStartIndex, seperator: mixSeparator, insert: "X"))
Here is another one, a little more straightforward.
let array = ["1","1","1","1","1","1","1","1","1","1","1","1","1","1","1"]
let result = array.enumerated().flatMap{ ($0.offset - 1) % 5 == 0 ? [$0.element, "X"] : [$0.element]}
print(result)
A functional solution to create an array where elements are inserted into the resulting array:
let result: [String] = array.enumerated().reduce(into:[]) { acc, elm in
let tempo = ((elm.offset - 2) % 5 == 0 ? ["X"] : []) + [elm.element]
acc.append(contentsOf: tempo)
}
If instead of inserting you'd like to replace the elements :
let result = array.enumerated().map {
($0.offset - 2) % 6 == 0 ? "X" : $0.element
}
Array.insert(_:at:)
We could use the built-in function Array.insert(_:at:)
let newCount = array.count > 2 ? array.count + 1 + (array.count - 3) / 6 : array.count
var index = 2
let mixSeparator = 6
var result = array
while index < newCount {
result.insert("X", at: index)
index += mixSeparator
}
But that would be inefficient, not only because you the whole array and then start inserting, but also because insert(_:at:) is an O(n) operation. Meaning that all the elements after the index of insertion should be shifted. A more efficient solution would be to do the insertion iteratively :
Iterative insert
Here is a pretty easy to understand iterative solution :
let array = ["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"]
var result = array
let x = "X"
let startIndex = 2
let step = 6
var count = 0
var index = 2
while index < array.count {
result[index] = x
count += 1
index += step
}
result.append(contentsOf: Array(repeating: "1", count: count))
Or, if you'd like to create the result array in one go :
let newCount = array.count > 2 ? array.count + 1 + (array.count - 3) / 6 : array.count
var result = [String]()
result.reserveCapacity(newCount)
let startIndex = 2
let step = 6
var nextIndexToPutX = startIndex
var index = 0
while index < newCount {
if index == nextIndexToPutX {
result.append("X")
nextIndexToPutX += step
} else {
result.append("1")
}
index += 1
}
Replace iteratively
var result = [String]()
result.reserveCapacity(array.count)
var nextIndexToPutX = 2
let step = 6
var index = 0
while index < array.count {
if index == nextIndexToPutX {
result.append("X")
nextIndexToPutX += step
} else {
result.append(array[index])
}
index += 1
}
I need to format a column of numbers in textView by displaying a column of fractional and decimal numbers and aligning them so the decimal point or forward slash characters appear in the same column like so ...
x 1/1
1 76.04900
x 193.15686
x 310.26471
4 5/4
x 503.42157
6 579.47057
x 696.57843
x 25/16
9 889.73529
x 1006.84314
11 1082.89214
This was achieved in Swift by inserting the appropriate number of leading whitespace characters and using left justification with a font where the column width is the same for each character.
To calculate the number of whitespaces, characters in the remainder or the dividend are excluded from the total number of characters in each number. This simple task involved subtracting .index(of: ) from .count and was complicated by the fact that .index(of:) returns an optional Array.Index? not an Int?
I suspect there might be a simpler way to achieve the same result and welcome any improvements to the Swift code below that might make life easier for whoever has to maintain it. The code was extracted from my project and has been tested in Playground.
Note. textMessage is a single string. It is currently displayed in the Debug area. It will eventually be displayed in a textView.
//: Playground
import UIKit
var tuningArray = ["1/1", "76.04900", "193.15686", "310.26471", "5/4", "503.42157", "579.47057", "696.57843", "25/16", "889.73529", "1006.84314", "1082.89214"]
var keyArray = ["x", "1", "x", "x", "4", "x", "6", "x", "x", "9", "x", "11"]
var characterCountArray = [Int]()
var scaleSize = Int()
var textMessage = String()
func formatTextMessage() {
var formattedTextArray = [String](repeating: "", count: scaleSize)
for i in 0..<scaleSize {
let element = tuningArray[i]
countNumericCharactersBeforeDotOrSlash (s: element)
}
let largestStringSize = characterCountArray.reduce(Int.min, { max($0, $1) })
for i in 0..<scaleSize {
let thisStringSize = characterCountArray[i]
let blanks = largestStringSize - thisStringSize
let filler = insertWhiteSpace(count: blanks)
formattedTextArray[i].append("\t" + keyArray[i] + "\t" + filler + tuningArray[i] + "\n")
textMessage += formattedTextArray[i]
}
print(textMessage)
}
func insertWhiteSpace(count: Int) -> String {
var filler = ""
for _ in 0..<count {
filler = filler + " "
}
return filler
}
func countNumericCharactersBeforeDotOrSlash (s: String) {
let fractionalNumber = findFractionalNumber(s: s)
let decimalNumber = findDecimalNumber(s: s)
var thisStringSize = 0
if !fractionalNumber && !decimalNumber {
print("Error: invalid number format")
}
if fractionalNumber == true {
let pitchStringArray = Array(s.characters)
let endIndex = pitchStringArray.count
if let slashIndex = pitchStringArray.index(of: "/") {
let dividendFiller = endIndex - slashIndex
thisStringSize = s.characters.count - dividendFiller
}
} else {
if decimalNumber == true {
let pitchStringArray = Array(s.characters)
let endIndex = pitchStringArray.count
if let dotIndex = pitchStringArray.index(of: ".") {
let remainderFiller = endIndex - dotIndex
thisStringSize = s.characters.count - remainderFiller
}
}
}
characterCountArray.append(thisStringSize)
}
func findDecimalNumber(s: String?) -> Bool {
if (s?.contains(".")) == true {
return true
}
return false
}
func findFractionalNumber(s: String?) -> Bool {
if (s?.contains("/")) == true {
return true
}
return false
}
scaleSize = tuningArray.count
formatTextMessage()
You could use attributed text with custom tab stops, like in this example:
import UIKit
import PlaygroundSupport
var tuningArray = ["1/1", "76.04900", "193.15686", "310.26471", "5/4", "503.42157", "579.47057", "696.57843", "25/16", "889.73529", "1006.84314", "1082.89214"]
var keyArray = ["x", "1", "x", "x", "4", "x", "6", "x", "x", "9", "x", "11"]
let scaleSize = tuningArray.count
var textMessage = ""
for i in 0..<scaleSize {
var textLine = "\t" + keyArray[i] + "\t" + tuningArray[i] + "\n"
textMessage += textLine
}
let paragraphStyle = NSMutableParagraphStyle()
let decimalTerminators:CharacterSet = [".", "/"]
let decimalTabOptions = [NSTabColumnTerminatorsAttributeName:decimalTerminators]
let decimalTabLocation = CGFloat(100) // TODO: Calculate...
var decimalTab = NSTextTab(textAlignment: .natural, location: decimalTabLocation, options: decimalTabOptions)
var leftTab = NSTextTab(textAlignment: .left, location: CGFloat(0.01), options: [:])
paragraphStyle.tabStops = [leftTab, decimalTab]
var a = NSAttributedString(string:textMessage,attributes: [NSParagraphStyleAttributeName: paragraphStyle])
let tv = UITextView(frame:CGRect(x:0, y:0, width:400, height:200))
tv.attributedText = a
PlaygroundPage.current.liveView = tv
The only drawback is, that you have to calculate the position of the decimal tab somehow; in the example I just use CGFloat(100) to get a nice value. But maybe you could simply live with a constant like this.
Greg,
Instead of using space to set columns use tab (\t) tree for times based on columns size you need.
That will help to manage code better way with very less and powerful coding.
Thanks
I'm trying to convert a string of morse code into words. I split the string into words. Then I split each word into a secondary array of letters and numbers.
def self.decode(str)
decoded = ""
arr = []
p arr = str.split(' ')
p arr.map! { |i| i.split(' ') }
arr.each do |r|
r.each do |i|
decoded += #morse.invert[i].to_s[-1]
end
end
p decoded
end
In my hash, I use numbers. They start with N. In to_s[-1], the -1 is to drop the N.
I get this error:
`+': no implicit conversion of nil into String
I can figure out how to get past it though because I don't see a nil value in the array.
class Morse
#morse = {
A: '.-',
B: '-...',
C: '-.-.',
D: '-..',
E: '.',
F: '..-.',
G: '--.',
H: '....',
I: '..',
J: '.---',
K: '-.-',
L: '.-..',
M: '--',
N: '-.',
O: '---',
P: '.--.',
Q: '--.-',
R: '.-.',
S: '...',
T: '-',
U: '..-',
V: '...-',
W: '.--',
X: '-..-',
Y: '-.--',
Z: '--..',
' ': ' ' ,
N1: '.----',
N2: '..---',
N3: '...--',
N4: '....-',
N5: '.....',
N6: '-....',
N7: '--...',
N8: '---..',
N9: '----.',
N0: '-----'
}
def self.encode(str)
encoded = ""
sym_temp = ""
str = str.upcase!
str.each_char do |c|
ascii_check = c.ord
if ascii_check.between?(65,90)
temp = str[c].to_sym
encoded += "#{#morse[temp]}" + " "
elsif ascii_check.between?(48,57)
temp = "N".concat(str[c]).to_sym
encoded += "#{#morse[temp]}" + " "
elsif ascii_check ===(32)
temp = str[c].to_sym
encoded += "#{#morse[temp]}"
end
end
p encoded
end
def self.decode(str)
decoded = ""
arr = []
# str.split(' ').map do |i|
p arr = str.split(' ')
p arr.map! { |i| i.split(' ') }
arr.each do |r|
r.each do |i|
p decoded += #morse.invert[i].to_s[-1].to_s
end
end
p decoded
end
# def self.read_file
# # #Temp = File.read("preamble_encode.txt").chomp
# # File.open('hiscore.txt'){|f| f.lines.map{|l| l.chomp!.split(',')}}
# # #Temp = File.foreach("preamble_encode.txt", 'r') {|f| f.lines.map{|l| l.chomp}}
# end
# def self.write_file
# # Temp2 = File.open('preamble_decode.txt', 'w') do |f|
# # f.puts Temp2
# # end
# end
end
test = "Abc 1oL!"
test2 = ".-- . - .... . .--. . --- .--. .-.. ."
Morse.encode(test)
Morse.decode(test2)
Your problem is here:
:P => :'.--.',
Your colon on the right hand side of the hash rocket is making that a symbol, not a string. As it happens, your test case for decode includes a P. When you try to look up the String '.--.', it doesn't find it and returns nil.
As you question has been answered, I would like to suggest how you may write methods for coding and decoding.
Firstly, the keys of your hash should be strings rather than symbols, as the hash is being used to code strings, which are made up of single-character strings. We also need the digits to be, '0' to '9' rather than 'N0' to 'N9'. Spaces are represented by time delays equal to seven dots, rather than combinations of dots and dashes, so I've represented spaces by the string '<delay>'. See the Morse Wiki.
Since the hash won't change, I've made it a constant rather than the value of an instance variable.
CharactersToMorse = {
'A' => '.-', 'B' => '-...', 'C' => '-.-.', 'D' => '-..', 'E' => '.', 'F' => '..-.',
'G' => '--.', 'H' => '....', 'I' => '..', 'J' => '.---', 'K' => '-.-', 'L' => '.-..',
'M' => '--' , 'N' => '-.', 'O' => '---', 'P' => '.--.', 'Q' => '--.-', 'R' => '.-.',
'S' => '...', 'T' => '-', 'U' => '..-', 'V' => '...-', 'W' => '.--', 'X' => '-..-',
'Y' => '-.--', 'Z' => '--..' , ' ' => '<delay>', '0' => '-----', '1' => '.----',
'2' => '..---', '3' => '...--', '4' => '....-', '5' => '.....', '6' => '-....',
'7' => '--...', '8' => '---..', '9' => '----.'
}
We will need to decode, so I'll assign the inverted hash to a constant as well.
MorseToCharacters = CharactersToMorse.invert
#=> {".-"=>"A", "-..."=>"B", "-.-."=>"C", "-.."=>"D", "."=>"E", "..-."=>"F",
# "--."=>"G", "...."=>"H", ".."=>"I", ".---"=>"J", "-.-"=>"K", ".-.."=>"L",
# "--"=>"M", "-."=>"N", "---"=>"O", ".--."=>"P", "--.-"=>"Q", ".-."=>"R",
# "..."=>"S", "-"=>"T", "..-"=>"U", "...-"=>"V", ".--"=>"W", "-..-"=>"X",
# "-.--"=>"Y", "--.."=>"Z", "<delay>"=>" ", "-----"=>"0", ".----"=>"1", "..---"=>"2",
# "...--"=>"3", "....-"=>"4", "....."=>"5", "-...."=>"6", "--..."=>"7",
# "---.."=>"8", "----."=>"9"}
Our methods for coding and decoding are now very simple.
def code(text)
text.each_char.map { |c| CharactersToMorse[c] }
end
def decode(morse)
morse.map { |m| MorseToCharacters[m] }.join(' ')
end
Let's try it.
text = "NOW IS THE TIME FOR 47 RUBIESTS TO COME TO THE AID OF THEIR BOWLING TEAM"
morse = code(text)
#=> ["-.", "---", ".--", "<delay>", "..", "...", "<delay>", "-", "....",
# ".", "<delay>", "-", "..", "--", ".", "<delay>", "..-.", "---", ".-.",
# "<delay>", "....-", "--...", "<delay>", ".-.", "..-", "-...", "..",
# ".", "...", "-", "...", "<delay>", "-", "---", "<delay>", "-.-.", "---",
# "--", ".", "<delay>", "-", "---", "<delay>", "-", "....", ".", "<delay>",
# ".-", "..", "-..", "<delay>", "---", "..-.", "<delay>", "-", "....",
# ".", "..", ".-.", "<delay>", "-...", "---", ".--", ".-..", "..", "-.",
# "--.", "<delay>", "-", ".", ".-", "--"]
decode(morse)
#=> "NOW IS THE TIME FOR 47 RUBIESTS TO COME TO THE AID OF THEIR BOWLING TEAM"
If for some reason the keys of CharactersToMorse must be symbols, change the methods to:
def code(text)
text.each_char.map { |c| CharactersToMorse[c.to_sym] }
end
def decode(morse)
morse.map { |m| MorseToCharacters[m].to_s }.join(' ')
end