# Convert Character Substrings to Numeric

I often have to take character data and categorize it numerically at work. A common thing I do is to take character type variables and convert them to numeric type characters, keeping same categories according to the level of work I'm doing. (The longer the substring, the more in depth and specific, shorter substrings for broad level). Enough backstory...

The challenge: In as few bytes as possible, convert the input part A, a vector/list of unique strings, into the output, a vector/list of numbers, keeping unique categories within the length of substrings the same length, which is input part B. Feel free to ask questions for clarification.

Input:

1. w, Vector/list of unique strings of equal character length. n <= 10

• These strings may be any combination of uppercase letters and numbers. Sorry if it seems my examples follow a pattern, I just created them after a similar pattern I see in the data I work with.
• Some random examples of what input could look like: "A1", "7LJ1", "J426SIR", "4AYE28TLSR", or any other random combination of uppercase letters up to 10 characters. To repeat: Each element in the vector/list will be the same length.
• Input may already be ordered by group, or non-contiguous, meaning elements lying within one group may be separated by elements of a different group. (See example 2)
2. s, where 1 <= s <= n

Output:

Output should have the indexes of the sorted, de-duplicated, trimmed values. See example 2. (I've included comments in my output to clarify, this is not required) This can be 0-based or 1-based index, as per your language uses.

Example Input 1:

#Input already alphabetized, but this input is not always guaranteed

s = 3, w =
[ABC01,
ABC11,
ABC21,
ABD01,
ABE01,
ABE02,
ACA10,
ACA11,
ACB20,
ACB21]


Example Output 1:

[1, #ABC
1,
1,
2, #ABD
3, #ABE
3,
4, #ACA
4,
5, #ACB
5]


Example Input 2: s = 4, w =

[X1Z123,
X1Z134,
X1Y123,
X1Y134,
X1Y145,
X1Y156,
X1X123,
X1X124,
X1X234,
X2Z123,
X2Z134,
X1X255,
X1Y124,
X2Z222,
X2Z223,
X2Z224]


Example 2 Output:

#Categorize by order of appearance
[1, #X1Z1
1,
2, #X1Y1
2,
2,
2,
3, #X1X1
3,
4, #X1X2
5, #X2Z1
5,
4, #X1X2 again (to show input can be non-contiguous)
2, #X1Y1 again
6, #X2Z2
6,
6]

OR

#Input not alphabetized, but indexes still match original input indexes.
[4, #X1Z1
4,
3, #X1Y1
3,
3,
3,
1, #X1X1
1,
2, #X1X2
5, #X2Z1
5,
2, #X1X2 again (to show input can be non-contiguous)
3, #X1Y1 again
6, #X2Z2
6,
6]

• May we use 0-based indexing for the output? – Shaggy Dec 24 '19 at 16:04
• I always forget about that. I'm used to 1-based indexing in my preferred language. Yes, I will allow that. – Sumner18 Dec 24 '19 at 16:13
• Can the input contain two strings with the same prefix in a non-contiguous block? For example, might we receive an input like [ABC12, ABC34, ABD45, ABC41] (ABD45 between strings of ABC**)? In this case, I would suggest adding a test case to address this. – Mr. Xcoder Dec 24 '19 at 16:37
• If the input is not sorted alphabetically does that imply our output should have the indexes of the sorted, de-duplicated, trimmed values or not? Example 2 pt 2 is confusing me. Maybe an example with unsorted input would be worth adding? – Jonathan Allan Dec 24 '19 at 18:50
• @JonathanAllan Sorry if my verbage isn't too clear, I'm trying to say that either output format works for any given non-alphabetically sorted input. Substring categories can be numbered in the order they appear or in the order they would appear if the substrings were sorted alphabetically, while still matching the indexes of the input. I'm still trying to learn Code Golf terminology and how to best describe things. – Sumner18 Dec 24 '19 at 19:30

# APL (Dyalog Extended), 87 6 bytesSBCS

Anonymous infix function. Takes s as left argument and w as a list of strings as right argument.

∪⍛⍳⍨↑¨


Try it online!

↑ take the first s characters from…
¨ each string in w

∪⍛⍨ in the list of the ‍nique strings in that…
⍳ find the ɩndex of the first occurrence of each string

• I'm always impressed with these Golfing languages. For once it would be great to stump a golfing language. – Sumner18 Dec 24 '19 at 15:25
• @Sumner18 APL absolutely isn't a golfing language, but rather an industrial strength production language detailed by a couple of ISO standards. (It even has an R bridge!) I'll be delighted to teach it to you over in the APL Orchard. – Adám Dec 24 '19 at 17:10
• Not only is it not a golfung language but in this challenge it may be more terse! – Jonathan Allan Dec 24 '19 at 18:37

# Japt, 9 bytes

®¯V
m!bUâ


Try it

• What even is Japt? I see that it is a shortened version of Javascript, but is it purely a golf language, or does it perform something more beyond Javascript's capabilities? – Sumner18 Dec 24 '19 at 16:18
• Hello @Shaggy ! I cannot understand why not simply using b instead of !b doesn't work ? Is it equivalent to £Uâ bX ? – AZTECCO Dec 24 '19 at 16:50

cut -c-$1|awk '{print(x=a[$0])?x:a[$0]=++b}'  Try it online! cut takes the first s characters, then awk remembers strings it's seen before and assigns them to ++b otherwise. # Jelly, 6 bytes ḣ€µQiⱮ  A dyadic Link accepting a list on the left and an integer on the right which yields a list of integers. Try it online! • You don’t need the Ṣ if I’ve understood the challenge correctly. I had ḣ€QiⱮ$ which is almost the same as yours so won’t post separately. – Nick Kennedy Dec 24 '19 at 18:40
• Example 2 part 2 seems to imply we need the sort – Jonathan Allan Dec 24 '19 at 18:42
• I understood it that you could sort alphabetically but it was optional. – Nick Kennedy Dec 24 '19 at 18:43

# Python 2, 94 65 bytes

def f(w,A):A=[a[:w]for a in A];return map(sorted(set(A)).index,A)


Try it online!

A whopping 29 bytes thx to Value Ink.

Uses 0-indexing; returns the index of the sorted list of possibilities (as per Example 2, alternate output).

• Actually, looking at Example 2's alternate output, list(set(A)) is probably sufficient for your purposes. – Value Ink Dec 25 '19 at 0:57
• @ValueInk: Ah, I missed that detail. I think it needs to be sorted(set(A)) though. – Chas Brown Dec 25 '19 at 1:07
• Yes, you are probably right. I just did a test of list(set(A)) on example 2 and found X1Y1 was first, when it should be third when sorted alphabetically. – Value Ink Dec 25 '19 at 1:15

# J, 1814 13 bytes

[:(~.i.]){."1


Try it online!

• {."1 Extract first s of each item
• (~.i.]) Within the uniq of that list ~. find the index of i. every item ]

# Ruby, 47 46 bytes

Uses 0-indexing in the order of appearance.

->s,w{w.map!{|e|e[0,s]}.map{|e|(w&w).index e}}


Try it online!

->s,w{w.map!{|e|e[0,s]}.map{|e|(w&w).index e}}
->s,w{                                       }  # Proc that takes 2 arguments
w.map!{|e|e[0,s]}                         # Replace each element of w with its prefix
.map{|e|             }   # For each element in the modified w
(w&w)            #  Deduplicated copy of the modified w
.index e    #  Get the element's index in the dedup copy


# Charcoal, 17 bytes

≧…θηＩＥη⌕Φη¬№…ημλι


Try it online! Link is to verbose version of code. Explanation:

≧…θη


Truncate each string to the given length.

ＩＥη⌕Φη¬№…ημλι


Map over each string, finding its position in the filtered list given by the first occurrence of each string in the list, and cast the results to string for implicit print.

# JavaScript (V8), 68 bytes

(z,L)=>(L=L.map(x=>x.slice(0,z))).map(x=>[...new Set(L)].indexOf(x))


Try it online!

0-indexed in order of appearance

(z,L)=>                      input : z= number of characters, L=list
(L=L.map(x=>x.slice(0,z)))   cut L saving it.
.map(x=>[... ].indexOf(x))   maps the result indexing in :
[...new Set(L)]      builds an iterable object out of the Set of L


# 05AB1E, 6 bytes

€£DÙsk


String-list $$\w\$$ as first input, and integer $$\s\$$ as second input.
Output is categorized by order of appearance, and uses 0-based indexing.

Output could also be categorized by alphabetical order for 6 bytes as well, by changing the Ù to ê.

Explanation:

€       # For each string in the (implicit) input-list:
£      #  Only leave the first (implicit) input-integer amount of characters
D     # Then duplicate this list of strings
Ù    # Uniquify the list
ê    # (Alternative: Uniquify AND sort the list)
s   # Swap to get the duplicated list again
k  # And get the (0-based) index of each in the unified list
# (after which the result is output implicitly)


# R, 40 bytes

function(w,s)c(factor(substring(w,1,s)))


Try it online!

c() has the odd behavior of coercing its arguments into the lowest possible one in the following hierarchy:

NULL < raw < logical < integer < real < complex < character < list < expression


factors are internally held as integers, so this coerces to an integer list.

• Wow you cut my possible R solution in half, though I don't want to answer my own questions. This is spectacular! Did you see my other question, Integer Keys and Duplicates. It was inspired by duplicated() where incomparables = k1 or k2 which results in some rather long code for a potential R response. I'm curious how you would approach that. – Sumner18 Jan 8 at 16:16
• @Sumner18 I did. I've been busy with work lately but I'll give it a go when I get a chance! – Giuseppe Jan 8 at 16:22
• @Sumner18 out of curiosity, how did you approach it? I knew that factor(substring(w,1,s)) would be the core of the challenge (and IIRC I commented as such in the sandbox) but even longer golfs have useful tricks in them, so I'd be remiss if I didn't ask. – Giuseppe Jan 10 at 16:19
• I appear to have deleted my code, but if I remember right, it was essentially the same approach. I converted the string to factor, then to integer, but using as.integer instead of c() – Sumner18 Jan 13 at 14:54