4

I have a string which looks like below

str = "I have candy='4' and ice cream = 'vanilla'"

I want to get terms to the left side of latest = and the terms should be fetched until the occurrence of another =.

So my string should be

leftOfEqual = "'4' and ice cream"

Another example

str = "I have candy='4' and ice cream = 'vanilla' and house='big'"

leftOfEqual = "'vanilla' and house"

This is my regex currently

leftOfEqual = str.match(/\S+(?= *=)/)[0]

But it looks at the first = and gives me only the immediate word to the left.

How can I do this?

NOTE: In case there is no presence = to the left of latest =, I should get the complete string till the beginning then.

7
  • How do we know that we need to include ice in ice cream which is left of the equals? Is and a key word to split by? Commented Jun 19, 2020 at 6:49
  • 1
    Does this help? Try .*=(.*)=.* and use $1 for replacement. Commented Jun 19, 2020 at 6:50
  • 1
    @Mandy8055 You should probably add that as an answer instead of a comment Commented Jun 19, 2020 at 6:51
  • 1
    @DaneBrouwer the idea is anything to the left of last = till the occurrence of another =, that piece of string should be fetched. Commented Jun 19, 2020 at 6:52
  • 1
    can't you just use .split('=')? Commented Jun 19, 2020 at 6:58

5 Answers 5

4

Using split and slice to find the second to last split group.
lastIndexOf solution, to just search from the back. Find first =, then continue to the next =, slice between them.

str = "I have candy='4' and ice cream = 'vanilla'"
console.log(
str.split('=').slice(-2)[0]
)

console.log(
str.slice(str.lastIndexOf('=',x=str.lastIndexOf('=')-1)+1,x<-1?undefined:x)
)

str = "and ice cream = 'vanilla'"
console.log(
str.split('=').slice(-2)[0]
)

console.log(
str.slice(str.lastIndexOf('=',x=str.lastIndexOf('=')-1)+1,x<-1?undefined:x)
)

str = "I have cand'4' and ice cream 'vanilla'"
console.log(
str.split('=').slice(-2)[0]
)

console.log(
str.slice(str.lastIndexOf('=',x=str.lastIndexOf('=')-1)+1,x<-1?undefined:x)
)

Sign up to request clarification or add additional context in comments.

3 Comments

this is probably the simplest and superb solution :) Why didn't this cross my mind!
@JvdV sure I will. I was just testing out the different answers and figuring out which one works the best. Will accept shortly.
@user120242 this works for all cases I tried so far and this one is simplest too. Thanks!
3

You can probably try:

.*=(.*)=.*|(.*)=.*

Explanation of the above regex:

.* - Matches everything except a newline before a = symbol greedily.

= - Matches = literally.

=(.*)= - Represents a capturing group capturing everything between =. You can use multiple symbols if you want by using them as character classes something like [=%#].

|(.*)=.* - Represents an alternate capturing group representing left hand side of the equal sign in case there is no = after the latest =.

$1$2 - You can use this as the replacement option for replacing the complete string with the required output.

You can find the demo of the above regex in here.


Pictorial Representation

const regex = /.*=(.*)=.*|(.*)=.*/g;
const str = `"I have candy='4' and ice cream = 'vanilla'"
I have candy='4' and ice cream = 'vanilla' and house='big'
Regex is fun
Regex is beauty =
Regex is awesome = test test test
`;
const subst = `$1$2`;

// The substituted value will be contained in the result variable
const result = str.replace(regex, subst);
// You can use trim if you want to get rid of previous or after spaces.
console.log(result);

4 Comments

you need ^$ if you are trying to match line by line. that's matching the whole string. Your regex needs to be adjusted to work with no = (he just added that requirement after you commented your solution I think?). You can make it work with [^=] matching
Thanks @user120242 for thes suggestion. But according to me if I use; replace then only the possible match will get replaced. Please see the sample code snippet which I attached. It is working for the string without = also. Please correct me if I'm wrong in unerstanding your comment.
Ah, you're using replace. I misread it. Works fine
@Mandy8055 your explanation is great. +1 for this.
2

Ultimately I think you could just use split and reverse with an extra trim:

str = "I have candy='4' and ice cream = 'vanilla' and house='big'"
leftOfEqual = str.split('=').reverse()[1].trim();
console.log(leftOfEqual)


But must you use regex one could use a pattern like:

[^=]+(?==[^=]*$)

See the Online Demo

  • [^=]+ - Negated equal sign, one or more times
  • (?==[^=]*$) - Positive lookahead for equal sign followed by negated equal sign zero or more times up to end string ancor.

enter image description here


This should also tick:

"In case there is no presence = to the left of latest =, I should get the complete string till the beginning then."

Comments

2

In your current pattern \S+(?= *=) you are matching 1+ non whitespace chars and assert what is on the right is an equals sign.

You might also use a capturing group and match the last = at the end of the string.

If there is no group 1 value available, then return the match, matching any char except a newline or =. Else return the group 1 value.

^(?:.*=([^\r\n=]+)=[^=\r\n]+$|[^\r\n=]+)

Explanation

  • ^ Start of string
  • .*= Match any char except a newline 0+ times until the last occurrence of =
  • [^\s=]+ Match any char except - or a whitespace char
  • \s*=\s* Match = between optional whitespace chars
  • ( Capture group 1
    • [^\r\n=]+ Match 1+ times any char except = or a newline
  • ) Close group 1
  • =[^=\r\n]+$ Match the last = at the end of the string.
  • | Or
  • [^\r\n=]+ Match 1+ times any char except a newline or =

Regex demo

const pattern = /^(?:.*=([^\r\n=]+)=[^=\r\n]+$|[^\r\n=]+)/;
[
  "I have candy='4' and ice cream = 'vanilla'",
  "I have candy='4' and ice cream = 'vanilla' and house='big'",
  "test test ='test test test'"
].forEach(s => {
  console.log(s.match(pattern)[1] || s.match(pattern)[0]);
});

5 Comments

@JvdV I saw that in the comments, currently if there is a single = it will return the whole value after it. So for test test ='test test test' it will return 'test test test' I can be wrong though, maybe it is not the expected outcome.
@Thefourthbird this is what I was wondering. It should return test test for the third one.
@JvdV I see that you mean, I think in that case you could use an alternation. I have updated it.
@SouvikRay I see that you mean, I have added an update.
@Thefourthbird I see it now. +1 for the detailed explanation of the regex.
1

You can use lookaround

const findData = (str) => {
  let data = str.match(/(?<==)([^=]+)(?==[^=]*$)/)
  return data ? data[1].trim() : ''
}

let str1 = "I have candy='4' and ice cream = 'vanilla' and house='big'"
let str2 = "I have candy='4' and ice cream = 'vanilla'"

console.log(findData(str1))
console.log(findData(str2))

// You can use split as well

console.log(str1.split('=').slice(-2)[0].trim())
console.log(str2.split('=').slice(-2)[0].trim())

3 Comments

Just a headsup, but if I'd like to test your regex in the console you'd need to rework your question for it to run properly. Though split, as mentioned before, is a good solution nonetheless. +
@JvdV are you using any browser which doesn't support lookbehind ? seems to work fine for me
Was talking about the anwer you put down. See what happens if you hit Run =).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.