I wanted to try something like this once
csv.py
that makes csv-like list
goodf.py
will do the trick.path
, but that is also appropriatef.py
def write(lines):
print 'write:', lines
csv.py
# -*- coding: utf-8 -*-
import f
def as_kvs(rows):
keys = rows[0]
value_rows = rows[1:]
return [dict(zip(keys, value_row)) for value_row in value_rows]
def write_first_one_or_empty(rows, sort_key, filter_key, filter_value, write_key):
#Make multiple kv
kvs = as_kvs(rows)
#Sort by specified key
_sorted = sorted(kvs, key=lambda row: row[sort_key])
#Filters only where the specified key has the specified value
_filtered = filter(lambda row: row[filter_key] == filter_value, _sorted)
if 0 < len(_filtered):
#Extract the first line with the specified key
_mapped_one = map(lambda row: row[write_key], _filtered)[0]
#Write
f.write([_mapped_one])
else:
#Create empty file
f.write([])
def write_all_or_empty(rows, filter_key, filter_value, write_key):
#Make multiple kv
kvs = as_kvs(rows)
#Filters only where the specified key has the specified value
_filtered = filter(lambda row: row[filter_key] == filter_value, kvs)
if _filtered:
#Extract all lines with the specified key
_mapped = map(lambda row: row[write_key], _filtered)
#Write
f.write(_mapped)
else:
#Create empty file
f.write([])
def write_all_or_error(rows, filter_key, filter_value, write_key):
#Make multiple kv
kvs = as_kvs(rows)
#Filters only where the specified key has the specified value
_filtered = filter(lambda row: row[filter_key] == filter_value, kvs)
if _filtered:
#Extract all lines with the specified key
_mapped = map(lambda row: row[write_key], _filtered)
#Write
f.write(_mapped)
else:
#error
raise Exception("no result")
main.py
# status,Give csv consisting of code
#Sort by code
#The first case where status is active
#Write out code
#If not, just create a file
csv.write_first_one_or_empty(
[['status', 'code'], ['dead', '001'], ['active', '003'], ['active', '002']],
'code', 'status', 'active', 'code'
)
#result
# write: ['002']
main.py
# name,Give a csv consisting of gender
#All cases where gender is male
#Write out name
#If not, just create a file
csv.write_all_or_empty(
[['name', 'gender'], ['Eva', 'female'], ['Sunny', 'female']],
'gender', 'male', 'name'
)
#result
# write: []
main.py
# status,Give csv consisting of tel
#All cases with dead status
#Export tel
#Otherwise an error
csv.write_all_or_error(
[['status', 'tel'], ['dead', '090-1111-1111'], ['active', '090-9999-9999']],
'status', 'dead', 'tel'
)
#result
# write: ['090-1111-1111']
def write_first_one_or_empty(rows, sort_key, filter_key, filter_value, write_key):
- #Make multiple kv
+ # as kvs
kvs = as_kvs(rows)
- #Sort by specified key
+ # sort by specified key
_sorted = sorted(kvs, key=lambda row: row[sort_key])
- #Filters only where the specified key has the specified value
+ # filter by specified key and value
_filtered = filter(lambda row: row[filter_key] == filter_value, _sorted)
if 0 < len(_filtered):
- #Extract the first line with the specified key
+ # extract by specified key and first one
_mapped_one = map(lambda row: row[write_key], _filtered)[0]
- #Write
+ # write
f.write([_mapped_one])
else:
- #Create empty file
+ # write empty
f.write([])
def write_all_or_empty(rows, filter_key, filter_value, write_key):
- #Make multiple kv
+ # as kvs
kvs = as_kvs(rows)
- #Filters only where the specified key has the specified value
+ # filter by specified key and value
_filtered = filter(lambda row: row[filter_key] == filter_value, kvs)
if _filtered:
- #Extract all lines with the specified key
+ # extract by specified key
_mapped = map(lambda row: row[write_key], _filtered)
- #Write
+ # write
f.write(_mapped)
else:
- #Create empty file
+ # write empty
f.write([])
def write_all_or_error(rows, filter_key, filter_value, write_key):
- #Make multiple kv
+ # as kvs
kvs = as_kvs(rows)
- #Filters only where the specified key has the specified value
+ # filter by specified key and value
_filtered = filter(lambda row: row[filter_key] == filter_value, kvs)
if _filtered:
- #Extract all lines with the specified key
+ # extract by specified key
_mapped = map(lambda row: row[write_key], _filtered)
- #Write
+ # write
f.write(_mapped)
else:
- #error
+ # error
raise Exception("no result")
# as kvs
and # write
.The method that was cut out is like this
+def sort_by_specified_key(kvs, key):
+ return sorted(kvs, key=lambda row: row[key])
+def filter_by_specified_key_and_value(kvs, key, value):
+ return filter(lambda row: row[key] == value, kvs)
+def extract_by_specified_key_and_first_one(kvs, key):
+ return kvs[0][key]
+def extract_by_specified_key(kvs, key):
+ return map(lambda row: row[key], kvs)
+def error():
+ raise Exception("no result")
The main body is like this
-def write_first_one_or_empty(rows, sort_key, filter_key, filter_value, write_key):
+def write_first_one_or_empty(rows, sort_key, filter_key, filter_value, extraction_key):
- # as kvs
kvs = as_kvs(rows)
- # sort by specified key
- _sorted = sorted(kvs, key=lambda row: row[sort_key])
+ _sorted = sort_by_specified_key(kvs, sort_key)
- # filter by specified key and value
- _filtered = filter(lambda row: row[filter_key] == filter_value, _sorted)
+ _filtered = filter_by_specified_key_and_value(_sorted, filter_key, filter_value)
if 0 < len(_filtered):
- # extract by specified key and first one
- _mapped_one = map(lambda row: row[write_key], _filtered)[0]
- # write
- f.write([_mapped_one])
+ extracted_one = extract_by_specified_key_and_first_one(_filtered, extraction_key)
+ f.write([extracted_one])
else:
- # write empty
f.write([])
-def write_all_or_empty(rows, filter_key, filter_value, write_key):
+def write_all_or_empty(rows, filter_key, filter_value, extraction_key):
- # as kvs
kvs = as_kvs(rows)
- # filter by specified key and value
- _filtered = filter(lambda row: row[filter_key] == filter_value, kvs)
+ _filtered = filter_by_specified_key_and_value(kvs, filter_key, filter_value)
if _filtered:
- # extract by specified key
- _mapped = map(lambda row: row[write_key], _filtered)
- # write
- f.write(_mapped)
+ extracted = extract_by_specified_key(_filtered, extraction_key)
+ f.write(extracted)
else:
- # write empty
f.write([])
-def write_all_or_error(rows, filter_key, filter_value, write_key):
+def write_all_or_error(rows, filter_key, filter_value, extraction_key):
- # as kvs
kvs = as_kvs(rows)
- # filter by specified key and value
- _filtered = filter(lambda row: row[filter_key] == filter_value, kvs)
+ _filtered = filter_by_specified_key_and_value(kvs, filter_key, filter_value)
if _filtered:
- # extract by specified key
- _mapped = map(lambda row: row[write_key], _filtered)
- # write
- f.write(_mapped)
+ extracted = extract_by_specified_key(_filtered, extraction_key)
+ f.write(extracted)
else:
- # error
- raise Exception("no result")
+ error()
==
filter_key
and filter_value
, so you can't filter like ʻage <20`I prepared head
and tail
because index access is a little unfriendly.
+def head(xs):
+ return xs[0]
+def tail(xs):
+ return xs[1:]
Where to use
def as_kvs(rows):
- keys = rows[0]
- value_rows = rows[1:]
+ keys = head(rows)
+ value_rows = tail(rows)
def extract_by_specified_key_and_first_one(kvs, key):
- return kvs[0][key]
+ return head(kvs)[key]
Changed filter_by_specified_key_and_value
to receive predicate
instead of key, value
to be a little more flexible
-def filter_by_specified_key_and_value(kvs, key, value):
- return filter(lambda row: row[key] == value, kvs)
+def filter_by_predicate(kvs, predicate):
+ return filter(predicate, kvs)
Then filter_by_predicate
does nothing more than filter
and discards it.
-def filter_by_predicate(kvs, predicate):
- return filter(predicate, kvs)
Then I made head
, so I may not have to prepare ʻextract_by_specified_key_and_first_one`.
If the names of the methods divided into small pieces are correct, it is difficult for the process to become unclear even if they are used in combination.
-def extract_by_specified_key_and_first_one(kvs, key):
- return head(kvs)[key]
Here is the main body that reflects the above
-def write_first_one_or_empty(rows, sort_key, filter_key, filter_value, extraction_key):
+def write_first_one_or_empty(rows, sort_key, predicate, extraction_key):
kvs = as_kvs(rows)
_sorted = sort_by_specified_key(kvs, sort_key)
- _filtered = filter_by_specified_key_and_value(_sorted, filter_key, filter_value)
+ _filtered = filter(predicate, _sorted)
if 0 < len(_filtered):
- extracted_one = extract_by_specified_key_and_first_one(_filtered, extraction_key)
+ extracted_one = head(_filtered)[extraction_key]
f.write([extracted_one])
else:
f.write([])
-def write_all_or_empty(rows, filter_key, filter_value, extraction_key):
+def write_all_or_empty(rows, predicate, extraction_key):
kvs = as_kvs(rows)
- _filtered = filter_by_specified_key_and_value(kvs, filter_key, filter_value)
+ _filtered = filter(predicate, kvs)
if _filtered:
extracted = extract_by_specified_key(_filtered, extraction_key)
f.write(extracted)
else:
f.write([])
-def write_all_or_error(rows, filter_key, filter_value, extraction_key):
+def write_all_or_error(rows, predicate, extraction_key):
kvs = as_kvs(rows)
- _filtered = filter_by_specified_key_and_value(kvs, filter_key, filter_value)
+ _filtered = filter(predicate, kvs)
if _filtered:
extracted = extract_by_specified_key(_filtered, extraction_key)
f.write(extracted)
else:
error()
filter
to f.write
in the bottom two of the three body methods is exactly the same, so cut it out.+def filter_and_extract_and_write_if_not_empty(kvs, predicate, extraction_key):
+ _filtered = filter(predicate, kvs)
+
+ if _filtered:
+ extracted = extract_by_specified_key(_filtered, extraction_key)
+ f.write(extracted)
Since it was cut out, the number of lines in the main body decreased
def write_all_or_empty(rows, predicate, extraction_key):
kvs = as_kvs(rows)
- _filtered = filter(predicate, kvs)
+ filter_and_extract_and_write_if_not_empty(kvs, predicate, extraction_key)
- if _filtered:
- extracted = extract_by_specified_key(_filtered, extraction_key)
- f.write(extracted)
- else:
+ if not kvs:
f.write([])
def write_all_or_error(rows, predicate, extraction_key):
kvs = as_kvs(rows)
- _filtered = filter(predicate, kvs)
+ filter_and_extract_and_write_if_not_empty(kvs, predicate, extraction_key)
- if _filtered:
- extracted = extract_by_specified_key(_filtered, extraction_key)
- f.write(extracted)
- else:
+ if not kvs:
error()
filter_and_extract_and_write_if_not_empty
is used If you have + ʻand
, they will often do it for you part was cut out, so the ʻelse
part remained on the main body side.The above-mentioned bad policy is cut back and redone
Pay attention to the second body method
This time, there is no need to explicitly separate the processing for the case with the contents of the list and the case with the empty list. Basically, if the processing to which the list is passed is the same, there is no need to be aware of the length of the list (it should not be).
It's better to pass this to f.write
without knowing if it's a content or an empty list
def write_all_or_empty(rows, predicate, extraction_key):
kvs = as_kvs(rows)
_filtered = filter(predicate, kvs)
- if _filtered:
- extracted = extract_by_specified_key(_filtered, extraction_key)
- f.write(extracted)
- else:
- f.write([])
+ extracted = extract_by_specified_key(_filtered, extraction_key)
+ f.write(extracted)
Next, pay attention to the first body method.
If this is not an empty list, the first element is taken out, processed, and listed again.
this
The flow
map
I will change the idea to the flow
First, prepare a method to get the matching first element from the list with a maximum length of 1.
+def find_first(kvs, predicate):
+ for kv in kvs:
+ if predicate(kv):
+ return [kv]
+ else:
+ return []
Then you don't have to worry about the contents like the second method of the main body method.
Moreover, since it is a list conversion, you can use ʻextract_by_specified_key instead of
headand
key` access.
def write_first_one_or_empty(rows, sort_key, predicate, extraction_key):
kvs = as_kvs(rows)
_sorted = sort_by_specified_key(kvs, sort_key)
+ first = find_first(_sorted, predicate)
- _filtered = filter(predicate, _sorted)
-
- if 0 < len(_filtered):
- extracted_one = head(extract_by_specified_key(_filtered, extraction_key))
- f.write([extracted_one])
- else:
- f.write([])
+ extracted = extract_by_specified_key(first, extraction_key)
+ f.write(extracted)
Both the ʻif clause and the ʻelse
clause of the main body methods 1 and 2 fit into the particle size of" writing process without being aware of the list length "
In a bad example, ʻif and ʻelse
were a group of export processes, but it was bad that only ʻif` was cut out.
csv
, but there is just a list operation processpublic
or private
Head
and tail
are likely to be used in other modules in the future, so public
l.py
and kick it out of csv.py
list.py
was fine, but the name conflicts with the standardlist ()
, so I shifted it appropriately)l.py
+# -*- coding: utf-8 -*-
+
+
+def head(xs):
+ return xs[0]
+
+
+def tail(xs):
+ return xs[1:]
+
+
+def find_first(xs, predicate):
+ for x in xs:
+ if predicate(x):
+ return [x]
+ else:
+ return []
kvs
to sort and extract, which is just a dictionary operation, so create d.py
and expel it from csv.py
.kvs
to an abstract appropriate name.d.py
+# -*- coding: utf-8 -*-
+
+
+def extract_by_specified_key(xs, key):
+ return map(lambda x: x[key], xs)
+
+
+def sort_by_specified_key(xs, key):
+ return sorted(xs, key=lambda x: x[key])
csv.py
, so make it private
-def as_kvs(rows):
+def __as_kvs(rows):
-def error():
+def __error():
kvs
is just a temporary data structure prepared by converting the data structure expected by csv.py
only to make it easier to process internally.
kvs
is actually dict
, but the dict
type does not appear in the public arguments or return value of csv.py
.
In other words, there is no dict
type as a function provided by the module, so it should not be exposed.
In addition, the argument type of ʻas_kvs is
list, but there are restrictions such as
header + [body]configuration and that all columns match, so
l.py and
I thought that it was preferable to make csv.py
private processing rather than d.py`
private
method is usedprivate
method that is used only once (maybe pros and cons here)When it comes to slightly larger modules, it can be extremely difficult to know where and how many private
s are used.
If you do so, it may be difficult to refactor the private
method or understand the range of influence, which can be quite difficult.
private
s, but each one is used only once.public 1 -> private 1 -> private 2 -> private 3
public a -> private a -> private b
public x -> private x
It is much easier to grasp the whole picture if you think that there are actually three public
s.
You can refactor private
with peace of mind.
private
, which is used only once, and private
, which depends on various parts.public 1 -> private 1
|
V
public a -> private a -> private b
^
|
public x -> private x -> private y
private 1
and private x
can be changed to some extent easily, but if you fix private b
lightly, it will affect all public
.
Try to define private
, which is used only from one place, in the method
This is the method I tried to do without permission, but there is no proper exhibition or discussion, but I think it is a good move and I use it a lot in disposable cords and solo projects
Since __error
is used only in one place, I prepared it with def
in def
By the way, I deleted the annoying __
because it became invisible from the outside anyway.
def write_all_or_error(rows, predicate, extraction_key):
+ def write_or_error(kvs):
+ if kvs:
+ f.write(kvs)
+ else:
+ raise Exception("no result")
+
kvs = __as_kvs(rows)
_filtered = filter(predicate, kvs)
extracted = d.extract_by_specified_key(_filtered, extraction_key)
- if extracted:
- f.write(extracted)
- else:
- __error()
+ write_or_error(extracted)
Of course, __as_kvs
is used from many places, so don't change it.
So the responsibility of the module is limited to "conversion" and the test is implemented.
It is decided to end with return
without" output "
Along with that, the method name was changed from write_
to ʻextract_`.
-def write_first_one_or_empty(rows, sort_key, predicate, extraction_key):
+def extract_first_one_or_empty(rows, sort_key, predicate, extraction_key):
kvs = __as_kvs(rows)
_sorted = d.sort_by_specified_key(kvs, sort_key)
first = l.find_first(_sorted, predicate)
- extracted = d.extract_by_specified_key(first, extraction_key)
- f.write(extracted)
+ return d.extract_by_specified_key(first, extraction_key)
-def write_all_or_empty(rows, predicate, extraction_key):
+def extract_all_or_empty(rows, predicate, extraction_key):
kvs = __as_kvs(rows)
_filtered = filter(predicate, kvs)
- extracted = d.extract_by_specified_key(_filtered, extraction_key)
- f.write(extracted)
+ return d.extract_by_specified_key(_filtered, extraction_key)
-def write_all_or_error(rows, predicate, extraction_key):
- def write_or_error(kvs):
- if kvs:
- f.write(kvs)
+def extract_all_or_error(rows, predicate, extraction_key):
+ def it_or_error(xs):
+ if xs:
+ return xs
else:
raise Exception("no result")
kvs = __as_kvs(rows)
_filtered = filter(predicate, kvs)
extracted = d.extract_by_specified_key(_filtered, extraction_key)
- write_or_error(extracted)
+ return it_or_error(extracted)
As a result, ʻimport disappears, so it can be confirmed that
csv.py` no longer processes file-io.
-from private import f
The test looks like this
csv_test.py
# -*- coding: utf-8 -*-
import csv
assert csv.extract_first_one_or_empty(
[['status', 'code'], ['dead', '001'], ['active', '003'], ['active', '002']],
'code', lambda kvs: kvs['status'] == 'active', 'code'
) == ['002']
assert csv.extract_first_one_or_empty(
[['status', 'code'], ['dead', '001']],
'code', lambda kvs: kvs['status'] == 'active', 'code'
) == []
csv.py
# -*- coding: utf-8 -*-
import l
import d
def __as_kvs(rows):
keys = l.head(rows)
value_rows = l.tail(rows)
return [dict(zip(keys, value_row)) for value_row in value_rows]
def extract_first_one_or_empty(rows, sort_key, predicate, extraction_key):
kvs = __as_kvs(rows)
_sorted = d.sort_by_specified_key(kvs, sort_key)
first = l.find_first(_sorted, predicate)
return d.extract_by_specified_key(first, extraction_key)
def extract_all_or_empty(rows, predicate, extraction_key):
kvs = __as_kvs(rows)
_filtered = filter(predicate, kvs)
return d.extract_by_specified_key(_filtered, extraction_key)
def extract_all_or_error(rows, predicate, extraction_key):
def it_or_error(xs):
if xs:
return xs
else:
raise Exception("no result")
kvs = __as_kvs(rows)
_filtered = filter(predicate, kvs)
extracted = d.extract_by_specified_key(_filtered, extraction_key)
return it_or_error(extracted)
or
for`, and all you have to do is call the method.def
, skip it and read from the beginning of the text)l.py
and d.py
that seem to be useful in the future were bornlist
type, the caller can perform further processing after this.I can't say it in a nutshell ...
return result #return result
, which is unexpectedly common# This process is for ~ ~ specifications
, etc.# ~ ~ was taken into consideration and implemented like this
, etc.private
instead of a comment and name it properly as in this example.private
methodprivate
methodprivate
method at the end should have a simple name and one thing to dolen ()
or head ()
or .split ()
private
of the facade that aggregates the private
is not so.head ()
every timeprivate
and public
methodspublic
s later.public
public methods properlyfilter
is set to find_first
and where it was filter
until the end and ʻif write else error
ʻif` partdef
in def
private
method created by converting a comment into a method is called from only one placescala can be obedient
def upperJoin(xs: List[String], sep: String): String = {
def upper(xs: List[String]): List[String] = xs.map(_.toUpperCase)
def join(xs: List[String]): String = xs.mkString(sep)
join(upper(xs))
}
upperJoin(List("hello", "world"), " ") // HELLO WORLD
Even in java, if you use Function <T, R>
etc.
public static String upperJoin(List<String> xs, String sep) {
Function<List<String>, List<String>> upper = _xs -> _xs.stream().map(String::toUpperCase).collect(toList());
Function<List<String>, String> join = _xs -> _xs.stream().collect(joining(sep));
return join.apply(upper.apply(xs));
}
upperJoin(asList("hello", "world"), " "); // HELLO WORLD
can also havekell Or rather, the idea comes from the sense of defining haskell functions by value.
upperJoin xs sep = (join . upper) xs
where
upper = map (map toUpper)
join = intercalate sep
upperJoin ["hello", "world"] " " -- HELLO WORLD
So the java example is close to haskell, and you can write it in python like this:
(In PEP 8, naming and binding lambda
is a violation of coding norms, but I hate nesting, so I'm more likely to be alone)
def upper_join(xs, sep):
upper = lambda xs: [x.upper() for x in xs]
join = lambda xs: sep.join(xs)
return join(upper(xs))
upper_join(['hello', 'world'], ' ') # HELLO WORLD
kvs
was dict
's list
, so it was a little different to put it on d.py
easily ...I just wanted to try it, so I was satisfied
It doesn't matter, but when I see the black letters on the red and green spaces, I always think that it looks like a watermelon.