Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Efficiently matching different possible substrings to the same value

Tags:

java

python

regex

I have a CSV file, one column of which is called Operating System and contains a string with values that look like this:

win-abc123
def456-windows
123123-WIN-ghi789
rhel-jkl012
45u8234dgf-redhat-mno345
pqr678-RHEL

In other words, the column value contains a substring somewhere inside of the string (front, middle, or end) indicating the operating system. The values could be one of win,windows,WIN,rhel,redhat,RHEL.

I want to examine the column value, and clean it up by replacing the entire column with either WIN or RHEL.

I have a clunky solution. Iterate over each row in the CSV, and iterate over each key, value pair in a operating system map. If it matches, replace the CSV value.

os_map = {'win':'WIN', 'windows': 'WIN', 'WIN':'WIN', 'rhel': 'RHEL', 'redhat': 'RHEL', 'RHEL': 'RHEL'}
for row in rows:
    os = row[OPERATING_SYSTEM]
    for key, value in os_map.iteritems():
        if key in os:
            row[OPERATING_SYSTEM] = value
            break

Or, in java:

Map<String, String> osMap = new HashMap<String, String>();
osMap.put("win", "WIN");
osMap.put("windows", "WIN");
osMap.put("WIN", "WIN");
// Repeat for RHEL values

String os;
for (String[] row : rows) {
    os = row[OPERATING_SYSTEM];

    for (Map.Entry<String, String> entry: osMap.entrySet()) {
        if (os.contains(entry.getKey())) {
            row[OPERATING_SYSTEM] = entry.getValue();
            break;
        }
    }
}

I don't like this because I'm iterating through the entire map (in the worst case) before I find a match. What is a more efficient way to solve this?

If the CSV columns were simply either win or windows, without the alphanumeric characters, I could instead do this:

os_map = {'win,windows,WIN': 'WIN', 'rhel,redhat,RHEL': 'RHEL'}
for key, value in os_map:
    if key.contains(row[OPERATING_SYSTEM]):
        row[OPERATING_SYSTEM] = value
        break

But this is not the case.

like image 859
Matthew Moisen Avatar asked Aug 24 '26 19:08

Matthew Moisen


1 Answers

In Python, you can do something along these lines:

test='''\
win-abc123
def456-windows
123123-WIN-ghi789
rhel-jkl012
45u8234dgf-redhat-mno345
pqr678-RHEL'''

from itertools import chain

os_map = {frozenset(['win', 'windows', 'WIN']):'WIN', frozenset(['rhel', 'redhat', 'RHEL',]): 'RHEL'}

all_os=set(chain(*os_map.keys()))

for line in test.splitlines():
    tgt=filter(lambda e: e in all_os,  line.split('-'))
    if tgt:
        print os_map[filter(lambda k: tgt[0] in k, os_map.keys())[0]]

You could also do a dict of regex:

import re    
os_reg={re.compile(r'\b(win|windows|WIN)\b'):'WIN', re.compile(r'\b(rhel|redhat|RHEL)\b'): 'RHEL'}        
for line in test.splitlines():
    for pat, v in os_reg.items():
        if pat.search(line):
            print line, v   
            break    

Or combine set and regex to do something like this:

os_map = {frozenset(['win', 'windows', 'WIN']):'WIN', frozenset(['rhel', 'redhat', 'RHEL',]): 'RHEL'}

for k, v in os_map.items():
    test=re.sub(r'\b({})\b'.format('|'.join(k)), v, test)

for line in test.splitlines():
    m=re.search(r'\b({})\b'.format('|'.join(os_map.values())), line)
    if m:
        print line, m.group(0)
like image 111
dawg Avatar answered Aug 26 '26 08:08

dawg



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!