5
\$\begingroup\$

A few days ago someone asked this question. But they quickly deleted the post offering no solution.
Given:

a = ['COP' , '\t\t\t', 'Basis', 'Notl', 'dv01', '6m', '9m', '1y',
     '18m', '2y', '3y', "15.6", 'mm', '4.6', '4y', '5y', '10', 'mm',
     '4.6', '6y', '7y', '8y', '9y', '10y', '20y', 'TOTAL', '\t\t9.2' ]

They needed to traverse the list and get the following 2D table. Where the first column has only values ending in "m" or "y".

"COB", "Basis", "Notl"
('6m', '', '')
('9m', '', '')
('1y', '', '')
('18m', '', '')
('2y', '', '')
('3y', '15.6', '')
('4y', '', '')
('5y', '10', '')
('6y', '', '')
('7y', '', '')
('8y', '', '')
('9y', '', '')
('10y', '', '')
('20y', '', '')

I answered the question, using a coroutine and a linked list as shown below:


a = ['COP' , '\t\t\t', 'Basis', 'Notl', 'dv01', '6m', '9m', '1y',
     '18m', '2y', '3y', "15.6", 'mm', '4.6', '4y', '5y', '10', 'mm',
     '4.6', '6y', '7y', '8y', '9y', '10y', '20y', 'TOTAL', '\t\t9.2' ]

headers = ('COP', "Basis", 'Notl', 'dv01')


def is_Number(value):
    is_number = False
    try:
        float(value)
    except ValueError:
        pass
    else:
        is_number = True
    return is_number


def is_cob(value):
    return True if (value[0].isnumeric() and (value[-1] == "m" or value[-1] == "y")) \
        else False


class Node:
    def __init__(self, data=None, next_cop=None, next_column=None):
        self.data = data
        self.next_cop = None
        self.next_column = None



class LinkedList:
    def __init__(self):
        self.headNode = None

    def printlist(self):
        print("This is the list =>:")
        temp = self.headNode
        while temp:
            print("Data-> :", temp.data)
            temp1 = temp.next_column
            while temp1:
                print("|")
                print("--->Column: Bias, Notl =", temp1.data)
                temp1 = temp1.next_column
            temp = temp.next_cop

    def isEmpty(self):
        return False if self.headNode else True

    def insert_at_head(self, node):
        if self.isEmpty():
            self.headNode = node
        else:
            node.next_cop = self.headNode
            self.headNode = node

    def insert_at_tail(self, node):
        if self.isEmpty():
            self.headNode = node
            return
        temp = self.headNode
        while temp.next_cop:
            temp = temp.next_cop
        temp.next_cop = node

    def insert_at_tail_column_of_nodeX(self, nodeX, node):
        temp = self.get_pointer_to_nodeX(nodeX)
        while temp.next_column:
            temp = temp.next_column
        temp.next_column = node

    def get_pointer_to_nodeX(self, nodeX):
        temp = self.headNode
        while temp.next_cop != nodeX:
            temp = temp.next_cop
        temp = temp.next_cop
        return temp

    def get_last_node_data(self):
        if self.isEmpty():
            return self.headNode.data
        temp = self.headNode
        while temp.next_cop:
            temp = temp.next_cop
        return temp.data

    def get_last_node(self):
        if self.isEmpty():
            return self.headNode
        temp = self.headNode
        while temp.next_cop:
            temp = temp.next_cop
        return temp

    def iterate_from(self, node=None):
        while node is not None:
            yield node
            node = node.next_cop


def rows(llist):
    lst = []
    while True:
        value = yield
        if value is None:
            break
        lst.append(value)
        is_number = is_Number(value)
        if is_cob(value):
            llist.insert_at_tail(Node(value))
        if is_number and is_cob(llist.get_last_node_data()) \
                and (is_cob(lst[-2]) or is_Number(lst[-2])):
            llist.insert_at_tail_column_of_nodeX(llist.get_last_node(), Node(value))
    return llist


if __name__ == "__main__":
    llist = LinkedList()

    sent_to_coro = [elem for elem in a if elem not in headers]
    coro_rows = rows(llist)
    next(coro_rows)
    for element in sent_to_coro:
        print("Sending....", element)
        coro_rows.send(element)
    try:
        coro_rows.send(None)
    except StopIteration as exc:
        result = exc.value

    out = []
    for item in llist.iterate_from(llist.headNode):
        cop = item.data
        basis = item.next_column.data if item.next_column else ""
        notl = item.next_column.next_column.data if basis and item.next_column.next_column else ""
        out.append((cop,basis,notl))
    for itm in out:
        print(itm)


Someone posted a solution with regex that was just a few lines of code, but unfortunately it was deleted because the question itself was just a question with no code and immediately started to be downvoted of course.

I would like to:

  1. Hear some suggestion on this code. Specifically, I wanted to know if there is any real improvement to this code.
  2. Is this solution overworked?
  3. I would like to see other solutions to this problem using Regex
\$\endgroup\$
4
  • \$\begingroup\$ Do you have a link to the original post. Even though it is deleted? \$\endgroup\$
    – Peilonrayz
    Jan 29, 2020 at 14:03
  • \$\begingroup\$ Unfortunately no. It was all so fast, I just can tell it was a couple of days ago and it was his/her first post in stackoverflow.com \$\endgroup\$ Jan 29, 2020 at 14:15
  • \$\begingroup\$ Let me clarify, that I answered the question but was not even able to post it. What I'm looking for is a review of my response and the answer that was posted that was using RegEx \$\endgroup\$ Jan 29, 2020 at 14:18
  • \$\begingroup\$ @Peilonrayz, I guess it is the link stackoverflow.com/questions/59940421/… \$\endgroup\$ Jan 29, 2020 at 14:23

2 Answers 2

2
\$\begingroup\$

To be brief on your three points.

You can try to omit extra variables and trust the function boundary. For example, your code starts with this excellently named function:

def is_Number(value):
    is_number = False
    try:
        float(value)
    except ValueError:
        pass
    else:
        is_number = True
    return is_number

or more concisely,

def is_Number(value):
   try:
      float(value)
   except ValueError:
      return False
   return True

You might see Code Like A Pythonista for a better explanation.

Yes, your code does seem overly worked for the problem.

Instead of giving you a regex, I recommend Regex101. It is a single web page playground for experimenting with RegEx with full explanations of what matches. Be sure to pick 'Python' for the dialect.

Keep hacking! Keep notes.

\$\endgroup\$
1
  • \$\begingroup\$ yes, this is a valid observation.Thanks \$\endgroup\$ Jan 31, 2020 at 16:41
0
\$\begingroup\$

With some help I found a solution using RegEx that indeed shows what I did is overworked. I failed to see the array as a long string and apply a RegEx to it

import re
a = ['COP' , '\t\t\t', 'Basis', 'Notl', 'dv01', '6m', '9m', '1y',
     '18m', '2y', '3y', "15.6", 'mm', '4.6', '4y', '5y', '10', 'mm',
     '4.6', '6y', '7y', '8y', '9y', '10y', '20y', 'TOTAL', '\t\t9.2']

   rule2 = re.compile(r"\b(\d+[ym])\W+([0-9]+\.?[0-9]*)*\b")
    a_str = " ".join(a)
    OUT2 = re.findall(rule2, a_str)
    print(OUT2)

output

>> [('6m', ''), ('9m', ''), ('1y', ''), ('18m', ''), ('2y', ''), ('3y', '15.6'), ('4y', ''), ('5y', '10'), ('6y', ''), ('7y', ''), ('8y', ''), ('9y', ''), ('10y', ''), ('20y', '')]
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.