List comprehension to find matching parens
I'm trying to come up with a list comprehension that will match open and close parens. So far I have these two statements which grab two lists of open and close parens separately
my_str = "hanz(and(franz/bob())+ 7) + tom(2)"
Grab idx of open parens:
[ i for i,c in enumerate(my_str) if c == '(']
# [4, 8, 18, 31]
Grab idx of close parens:
[ i for i,c in enumerate(my_str) if c == ')']
# [19, 20, 24, 33]
What I would like is one handy comprehension that could give me a list of pairs corresponding to each match pair of parens
i.e.
[ ???? for i,c in enumerate(my_str) ???]
# [(4,24), (8,20), (18,19), (31,33)]
python list-comprehension
add a comment |
I'm trying to come up with a list comprehension that will match open and close parens. So far I have these two statements which grab two lists of open and close parens separately
my_str = "hanz(and(franz/bob())+ 7) + tom(2)"
Grab idx of open parens:
[ i for i,c in enumerate(my_str) if c == '(']
# [4, 8, 18, 31]
Grab idx of close parens:
[ i for i,c in enumerate(my_str) if c == ')']
# [19, 20, 24, 33]
What I would like is one handy comprehension that could give me a list of pairs corresponding to each match pair of parens
i.e.
[ ???? for i,c in enumerate(my_str) ???]
# [(4,24), (8,20), (18,19), (31,33)]
python list-comprehension
6
This isn't really a job for a list comprehension; is there a reason for it? It's extremely easy with a loop and a stack.
– Tordek
Nov 15 '18 at 2:54
add a comment |
I'm trying to come up with a list comprehension that will match open and close parens. So far I have these two statements which grab two lists of open and close parens separately
my_str = "hanz(and(franz/bob())+ 7) + tom(2)"
Grab idx of open parens:
[ i for i,c in enumerate(my_str) if c == '(']
# [4, 8, 18, 31]
Grab idx of close parens:
[ i for i,c in enumerate(my_str) if c == ')']
# [19, 20, 24, 33]
What I would like is one handy comprehension that could give me a list of pairs corresponding to each match pair of parens
i.e.
[ ???? for i,c in enumerate(my_str) ???]
# [(4,24), (8,20), (18,19), (31,33)]
python list-comprehension
I'm trying to come up with a list comprehension that will match open and close parens. So far I have these two statements which grab two lists of open and close parens separately
my_str = "hanz(and(franz/bob())+ 7) + tom(2)"
Grab idx of open parens:
[ i for i,c in enumerate(my_str) if c == '(']
# [4, 8, 18, 31]
Grab idx of close parens:
[ i for i,c in enumerate(my_str) if c == ')']
# [19, 20, 24, 33]
What I would like is one handy comprehension that could give me a list of pairs corresponding to each match pair of parens
i.e.
[ ???? for i,c in enumerate(my_str) ???]
# [(4,24), (8,20), (18,19), (31,33)]
python list-comprehension
python list-comprehension
asked Nov 15 '18 at 2:37
darkpbjdarkpbj
1,12521326
1,12521326
6
This isn't really a job for a list comprehension; is there a reason for it? It's extremely easy with a loop and a stack.
– Tordek
Nov 15 '18 at 2:54
add a comment |
6
This isn't really a job for a list comprehension; is there a reason for it? It's extremely easy with a loop and a stack.
– Tordek
Nov 15 '18 at 2:54
6
6
This isn't really a job for a list comprehension; is there a reason for it? It's extremely easy with a loop and a stack.
– Tordek
Nov 15 '18 at 2:54
This isn't really a job for a list comprehension; is there a reason for it? It's extremely easy with a loop and a stack.
– Tordek
Nov 15 '18 at 2:54
add a comment |
2 Answers
2
active
oldest
votes
Like @Tordek mentioned, though not impossible this is not very practical,
For the sake of completeness however, here's a solution:
my_str = "hanz(and(franz/bob())+ 7) + tom(2)"
pt_arr = [ 1 if c == '(' else -1 for i,c in enumerate(my_str ) if c == ')' or c == '(']
idx_arr = [ i for i,c in enumerate(my_str ) if c == ')' or c == '(']
[(idx_arr[strt_idx],idx_arr[strt_idx + [j for j,d in enumerate([ sum(pt_arr[strt_idx:i + 1]) for i,c in enumerate(pt_arr) if i >= strt_idx]) if d == 0][0]]) for strt_idx,f in enumerate(pt_arr) if f == 1]
# [(4,24), (8,20), (18,19), (31,33)]
add a comment |
As mentioned in comments, the proper and simple way to do this is using a stack:
my_str = "hanz(and(franz/bob())+ 7) + tom(2)"
stack =
parens =
for i, c in enumerate(my_str):
if c == "(":
stack.append(i)
elif c == ")":
parens.append((stack.pop(), i))
print(parens) # [(18, 19), (8, 20), (4, 24), (31, 33)]
But if you value one-liners more than readability or coding conventions, you could also cram that into a list comprehensions with side effects:
stack =
parens = [(stack.pop(), i) for i, c in enumerate(my_str)
if c == "(" and stack.append(i) or c == ")"]
print(parens) # [(18, 19), (8, 20), (4, 24), (31, 33)]
This uses the fact that and
and or
are short-circuit-evaluated, thus it will append
items only if c == "("
, but then fail because append
return None
, and only add elements to the results if the second condition, c == ")"
is true, popping the position of the most recent (
from the stack.
At least, it is not a total abuse of list comprehensions, as the result is not discarded but actually the desired result, and it's probably still easier to comprehend than the three list comprehensions that you have (although those work without side effects), but the better solution for a "handy" way to do this would be: Make it a function, than it does not matter how many lines it has.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53311626%2flist-comprehension-to-find-matching-parens%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
Like @Tordek mentioned, though not impossible this is not very practical,
For the sake of completeness however, here's a solution:
my_str = "hanz(and(franz/bob())+ 7) + tom(2)"
pt_arr = [ 1 if c == '(' else -1 for i,c in enumerate(my_str ) if c == ')' or c == '(']
idx_arr = [ i for i,c in enumerate(my_str ) if c == ')' or c == '(']
[(idx_arr[strt_idx],idx_arr[strt_idx + [j for j,d in enumerate([ sum(pt_arr[strt_idx:i + 1]) for i,c in enumerate(pt_arr) if i >= strt_idx]) if d == 0][0]]) for strt_idx,f in enumerate(pt_arr) if f == 1]
# [(4,24), (8,20), (18,19), (31,33)]
add a comment |
Like @Tordek mentioned, though not impossible this is not very practical,
For the sake of completeness however, here's a solution:
my_str = "hanz(and(franz/bob())+ 7) + tom(2)"
pt_arr = [ 1 if c == '(' else -1 for i,c in enumerate(my_str ) if c == ')' or c == '(']
idx_arr = [ i for i,c in enumerate(my_str ) if c == ')' or c == '(']
[(idx_arr[strt_idx],idx_arr[strt_idx + [j for j,d in enumerate([ sum(pt_arr[strt_idx:i + 1]) for i,c in enumerate(pt_arr) if i >= strt_idx]) if d == 0][0]]) for strt_idx,f in enumerate(pt_arr) if f == 1]
# [(4,24), (8,20), (18,19), (31,33)]
add a comment |
Like @Tordek mentioned, though not impossible this is not very practical,
For the sake of completeness however, here's a solution:
my_str = "hanz(and(franz/bob())+ 7) + tom(2)"
pt_arr = [ 1 if c == '(' else -1 for i,c in enumerate(my_str ) if c == ')' or c == '(']
idx_arr = [ i for i,c in enumerate(my_str ) if c == ')' or c == '(']
[(idx_arr[strt_idx],idx_arr[strt_idx + [j for j,d in enumerate([ sum(pt_arr[strt_idx:i + 1]) for i,c in enumerate(pt_arr) if i >= strt_idx]) if d == 0][0]]) for strt_idx,f in enumerate(pt_arr) if f == 1]
# [(4,24), (8,20), (18,19), (31,33)]
Like @Tordek mentioned, though not impossible this is not very practical,
For the sake of completeness however, here's a solution:
my_str = "hanz(and(franz/bob())+ 7) + tom(2)"
pt_arr = [ 1 if c == '(' else -1 for i,c in enumerate(my_str ) if c == ')' or c == '(']
idx_arr = [ i for i,c in enumerate(my_str ) if c == ')' or c == '(']
[(idx_arr[strt_idx],idx_arr[strt_idx + [j for j,d in enumerate([ sum(pt_arr[strt_idx:i + 1]) for i,c in enumerate(pt_arr) if i >= strt_idx]) if d == 0][0]]) for strt_idx,f in enumerate(pt_arr) if f == 1]
# [(4,24), (8,20), (18,19), (31,33)]
answered Nov 15 '18 at 16:37
darkpbjdarkpbj
1,12521326
1,12521326
add a comment |
add a comment |
As mentioned in comments, the proper and simple way to do this is using a stack:
my_str = "hanz(and(franz/bob())+ 7) + tom(2)"
stack =
parens =
for i, c in enumerate(my_str):
if c == "(":
stack.append(i)
elif c == ")":
parens.append((stack.pop(), i))
print(parens) # [(18, 19), (8, 20), (4, 24), (31, 33)]
But if you value one-liners more than readability or coding conventions, you could also cram that into a list comprehensions with side effects:
stack =
parens = [(stack.pop(), i) for i, c in enumerate(my_str)
if c == "(" and stack.append(i) or c == ")"]
print(parens) # [(18, 19), (8, 20), (4, 24), (31, 33)]
This uses the fact that and
and or
are short-circuit-evaluated, thus it will append
items only if c == "("
, but then fail because append
return None
, and only add elements to the results if the second condition, c == ")"
is true, popping the position of the most recent (
from the stack.
At least, it is not a total abuse of list comprehensions, as the result is not discarded but actually the desired result, and it's probably still easier to comprehend than the three list comprehensions that you have (although those work without side effects), but the better solution for a "handy" way to do this would be: Make it a function, than it does not matter how many lines it has.
add a comment |
As mentioned in comments, the proper and simple way to do this is using a stack:
my_str = "hanz(and(franz/bob())+ 7) + tom(2)"
stack =
parens =
for i, c in enumerate(my_str):
if c == "(":
stack.append(i)
elif c == ")":
parens.append((stack.pop(), i))
print(parens) # [(18, 19), (8, 20), (4, 24), (31, 33)]
But if you value one-liners more than readability or coding conventions, you could also cram that into a list comprehensions with side effects:
stack =
parens = [(stack.pop(), i) for i, c in enumerate(my_str)
if c == "(" and stack.append(i) or c == ")"]
print(parens) # [(18, 19), (8, 20), (4, 24), (31, 33)]
This uses the fact that and
and or
are short-circuit-evaluated, thus it will append
items only if c == "("
, but then fail because append
return None
, and only add elements to the results if the second condition, c == ")"
is true, popping the position of the most recent (
from the stack.
At least, it is not a total abuse of list comprehensions, as the result is not discarded but actually the desired result, and it's probably still easier to comprehend than the three list comprehensions that you have (although those work without side effects), but the better solution for a "handy" way to do this would be: Make it a function, than it does not matter how many lines it has.
add a comment |
As mentioned in comments, the proper and simple way to do this is using a stack:
my_str = "hanz(and(franz/bob())+ 7) + tom(2)"
stack =
parens =
for i, c in enumerate(my_str):
if c == "(":
stack.append(i)
elif c == ")":
parens.append((stack.pop(), i))
print(parens) # [(18, 19), (8, 20), (4, 24), (31, 33)]
But if you value one-liners more than readability or coding conventions, you could also cram that into a list comprehensions with side effects:
stack =
parens = [(stack.pop(), i) for i, c in enumerate(my_str)
if c == "(" and stack.append(i) or c == ")"]
print(parens) # [(18, 19), (8, 20), (4, 24), (31, 33)]
This uses the fact that and
and or
are short-circuit-evaluated, thus it will append
items only if c == "("
, but then fail because append
return None
, and only add elements to the results if the second condition, c == ")"
is true, popping the position of the most recent (
from the stack.
At least, it is not a total abuse of list comprehensions, as the result is not discarded but actually the desired result, and it's probably still easier to comprehend than the three list comprehensions that you have (although those work without side effects), but the better solution for a "handy" way to do this would be: Make it a function, than it does not matter how many lines it has.
As mentioned in comments, the proper and simple way to do this is using a stack:
my_str = "hanz(and(franz/bob())+ 7) + tom(2)"
stack =
parens =
for i, c in enumerate(my_str):
if c == "(":
stack.append(i)
elif c == ")":
parens.append((stack.pop(), i))
print(parens) # [(18, 19), (8, 20), (4, 24), (31, 33)]
But if you value one-liners more than readability or coding conventions, you could also cram that into a list comprehensions with side effects:
stack =
parens = [(stack.pop(), i) for i, c in enumerate(my_str)
if c == "(" and stack.append(i) or c == ")"]
print(parens) # [(18, 19), (8, 20), (4, 24), (31, 33)]
This uses the fact that and
and or
are short-circuit-evaluated, thus it will append
items only if c == "("
, but then fail because append
return None
, and only add elements to the results if the second condition, c == ")"
is true, popping the position of the most recent (
from the stack.
At least, it is not a total abuse of list comprehensions, as the result is not discarded but actually the desired result, and it's probably still easier to comprehend than the three list comprehensions that you have (although those work without side effects), but the better solution for a "handy" way to do this would be: Make it a function, than it does not matter how many lines it has.
edited Nov 15 '18 at 16:58
answered Nov 15 '18 at 16:49
tobias_ktobias_k
59.1k971110
59.1k971110
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53311626%2flist-comprehension-to-find-matching-parens%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
6
This isn't really a job for a list comprehension; is there a reason for it? It's extremely easy with a loop and a stack.
– Tordek
Nov 15 '18 at 2:54