Hledání a nahrazování pomocí JavaScriptu

gleng

Hledání a nahrazování pomocí JavaScriptu
« kdy: 05. 01. 2022, 13:02:13 »
Mam retazec ktory ma v sebe vyrazy ktore potrebujem najst a nahradit. Priklad:

Citace
@john and @johnathan went to see @sarah in their #hometown to look at her new #home.

Priklad vyrazov:
Kód: [Vybrat]
let handles = ["john", "johnathan", "sarah"]
let topics = ["home", "hometown"]

A chcem nahradit handles s
Kód: [Vybrat]
<a href="/user/john">@john</a> a topics s
Kód: [Vybrat]
<a href="/topic/hometown">#hometown</a>.

Potrebujem najst a nahradit vyrazy bez ohladu na velkost pismen a taktiez potrebujem ignorovat vyrazy ktore nie su v zozname. Dalej potrebujem zarucit ze ked sa mi prekryvaju tokeny(john -> johnathan) tak sa vyrazy najdu a nahradia spravne.

Ako by ste to napisali?
« Poslední změna: 05. 01. 2022, 13:13:34 od Petr Krčmář »


Re:Hledání a nahrazování pomocí JavaScriptu
« Odpověď #1 kdy: 05. 01. 2022, 13:35:46 »
Seřadit tokeny od nejdelšího po nejkratší a pak jeden po druhém nahrazovat.

gleng

Re:Hledání a nahrazování pomocí JavaScriptu
« Odpověď #2 kdy: 05. 01. 2022, 14:14:15 »
Seřadit tokeny od nejdelšího po nejkratší a pak jeden po druhém nahrazovat.

ano, a skoncis v nekonecnej slucke...
Kód: [Vybrat]
<a href="/user/john">
     <a href="/user/john">
           <a href="/user/john">
                 <a href="/user/john">...</a>
           </a>
     </a>
</a>

Keby to bolo take lahke tak sa tu nepytam.

robac

  • ***
  • 198
    • Zobrazit profil
    • E-mail
Re:Hledání a nahrazování pomocí JavaScriptu
« Odpověď #3 kdy: 05. 01. 2022, 14:54:03 »
Obecně řešení pana Jirsáka není dobře (v textu nahradím řetězec X za Y, čímž v něm vznikne řetězec Z, který je v seznamu tokenů pro další zpracování).

V tomto konkrétním případně by se to dalo asi bezpečně ošetřit.

Pro Python se mi zdá OK:
https://stackoverflow.com/a/15448887

Otázka je, jak text vypadá a jestli lze skutečně použít takto jednoduché řešení.

gleng

Re:Hledání a nahrazování pomocí JavaScriptu
« Odpověď #4 kdy: 05. 01. 2022, 14:54:45 »
SO odpoved ak by niekto potreboval do buducna:
Kód: [Vybrat]
var tmpString = `@john and @johnathan went to see @sarah in their #hometown to look at her new #home.`,
handles = ["john", "johnathan", "sarah"],
topics = ["home", "hometown"];
console.log(tmpString);

for(var i = 0; i < handles.length; i++) tmpString = tmpString.replace(new RegExp(`(@${handles[i]})+(?![A-Za-z0-9])`, 'gmi'), `<a href="/user/${handles[i]}">@${handles[i]}</a>`);
console.log(tmpString);
for(var i = 0; i < topics.length; i++) tmpString = tmpString.replace(new RegExp(`(#${topics[i]})+(?![A-Za-z0-9])`, 'gmi'), `<a href="/topics/${topics[i]}">#${topics[i]}</a>`);
console.log(tmpString);


Idris

  • *****
  • 2 286
    • Zobrazit profil
    • E-mail
Re:Hledání a nahrazování pomocí JavaScriptu
« Odpověď #5 kdy: 05. 01. 2022, 15:03:55 »
Seřadit tokeny od nejdelšího po nejkratší a pak jeden po druhém nahrazovat.
ano, a skoncis v nekonecnej slucke...
Kód: [Vybrat]
<a href="/user/john">
     <a href="/user/john">
           <a href="/user/john">
                 <a href="/user/john">...</a>
           </a>
     </a>
</a>

Keby to bolo take lahke tak sa tu nepytam.
Na tohle existují specializované algoritmy, například Aho-Corasickové, které pracují v optimálním čase. Na VŠ se zrovna tento bere hned v prváku.
« Poslední změna: 05. 01. 2022, 15:05:46 od Idris »

Re:Hledání a nahrazování pomocí JavaScriptu
« Odpověď #6 kdy: 05. 01. 2022, 15:06:08 »
SO odpoved ak by niekto potreboval do buducna:

Než jsem vám stihl odpovědět, že máte neúplné zadání, tak jste si našel odpověď. Zrada, proč vaše zadání nebude fungovat je nejasná definice chování replace. Ono #home je substringem #hometown a tudíž by se to nejspíš mělo zacyklit. Tak zní zadání.

Vámi postnuté řešení je elegantní a funkční, protože jste do něj doplnil chybějící podmínku. A sice, že za tokenem následuje znak, co není písmenem ani číslicí. Tohle je to, čím jste zadání doplnil a problém vyřešil: (?![A-Za-z0-9])

Od té chvíle se vám #home a #hometown budou chovat jako dva různé tokeny. Ovšem jde o jiné chování než vaše původní zadání, kupříkladu nebude reagovat na #homeless, ačkoliv tam #home zcela zjevně je.

Re:Hledání a nahrazování pomocí JavaScriptu
« Odpověď #7 kdy: 05. 01. 2022, 15:38:20 »
SO odpoved ak by niekto potreboval do buducna:
Kód: [Vybrat]
var tmpString = `@john and @johnathan went to see @sarah in their #hometown to look at her new #home.`,
handles = ["john", "johnathan", "sarah"],
topics = ["home", "hometown"];
console.log(tmpString);

for(var i = 0; i < handles.length; i++) tmpString = tmpString.replace(new RegExp(`(@${handles[i]})+(?![A-Za-z0-9])`, 'gmi'), `<a href="/user/${handles[i]}">@${handles[i]}</a>`);
console.log(tmpString);
for(var i = 0; i < topics.length; i++) tmpString = tmpString.replace(new RegExp(`(#${topics[i]})+(?![A-Za-z0-9])`, 'gmi'), `<a href="/topics/${topics[i]}">#${topics[i]}</a>`);
console.log(tmpString);

lze to udělat i takhle:

Kód: [Vybrat]
const regex_topics = /#([a-zA-Z0-9_]+)/ig
const regex_handles= /@([a-zA-Z0-9_]+)/ig
let text = "@john and @johnathan went to see @sarah in their #hometown to look at her new #home."

const handles = ["john", "johnathan", "sarah"]
const topics = ["home", "hometown"]

function createAnchor(url, text) {
  let c = document.createTextNode(text);
  let a = document.createElement('a');
  a.setAttribute("href", url);
  a.appendChild(c);
  return a.outerHTML;
}

text = text.replace(regex_topics, function(value) {
  let i = topics.indexOf(value.substring(1).toLowerCase())
  if (i>-1) {
    return createAnchor("/topic/"+topics[i], value)
  } else {
    return value
  }
});

text = text.replace(regex_handles, function(value) {
  let i = handles.indexOf(value.substring(1).toLowerCase())
  if (i>-1) {
    return createAnchor("/user/"+handles[i], value)
  } else {
    return value
  }
});

Vyhneš vyhledávání přes regulární výraz ve smyčce, drahýmu dopřednými hledání, stejně tak mám ošetřené escapování a vstupní hodnoty, do url dosazuji vždy variantu s malými znaky (v tvém řešení se ti budou velká písmena přepisovat i do url).

tecka

  • ***
  • 138
    • Zobrazit profil
    • E-mail
Re:Hledání a nahrazování pomocí JavaScriptu
« Odpověď #8 kdy: 05. 01. 2022, 15:49:10 »
SO odpoved ak by niekto potreboval do buducna:
Na test "konce slova" v regexu je \b.

Ovšem jde o jiné chování než vaše původní zadání, kupříkladu nebude reagovat na #homeless, ačkoliv tam #home zcela zjevně je.
On ale nechce homeless detekovat jako home.


Re:Hledání a nahrazování pomocí JavaScriptu
« Odpověď #9 kdy: 05. 01. 2022, 15:51:34 »
ano, a skoncis v nekonecnej slucke...

Kód: [Vybrat]
<a href="/user/john">
     <a href="/user/john">
           <a href="/user/john">
                 <a href="/user/john">...</a>
           </a>
     </a>
</a>
Ne, neskončím. Když nahrazuju @john za john, nemůžu skončit v nekonečné smyčce. Dokonce neskončím v nekonečné smyčce ani při nahrazování @john ta @john, protože všechny funkce replaceAll() ve všech jazycích si vždy pamatují pozici, kde text nahradily naposledy, a pokračují dále, takže se nezacyklí.

Keby to bolo take lahke tak sa tu nepytam.
Nejkomplikovanější na tom evidentně bude zadání. Když chcete něco nahrazovat v textu, dejte sem příklad vstupu a odpovídajícího výstupu.

Pokud máte problém s tím, že chcete nahrazovat @john → xxx@johnxxx a @johnatan → xxx@johnatanxxx, ale když už jednou nahradíte @johnatana, nechcete to pak znovu nahrazovat jako @johna, pak si prostě místo nahrazování jenom zapamatujte pozice, kde se má něco nahradit, a náhradu proveďte až na konec. To zapamatování pozic je nejjednoduší udělat tak, že si vytvoříte seznam, kde se budou střídat textové části a náhrady, a v příštím kole budete nahrazovat jen v textových částech.

Začnete tedy se vstupem:

Kód: [Vybrat]
[
  "@john and @johnathan went to see @sarah in their #hometown to look at her new #home.",
]

Nahradíte @johnathan:

Kód: [Vybrat]
[
  "@john and ",
  {replace: "@johnathan"},
  " went to see @sarah in their #hometown to look at her new #home.",
]

Pokračujete náhradou @john ve zbývajících textech,

Kód: [Vybrat]
[
  {replace: "@john"},
  " and ",
  {replace: "@johnathan"},
  " went to see @sarah in their #hometown to look at her new #home.",
]

Na závěr pole projdete, texty necháte jak jsou a objekty označující náhradu nahradíte nahrazujícím textem. A pak už jen pole spojíte do stringu.

Celé to samozřejmě děláte tak, že pole nahrazovaných textů je seřazené podle délky od nejdelšího, jak jsem psal na začátku.

tecka

  • ***
  • 138
    • Zobrazit profil
    • E-mail
Re:Hledání a nahrazování pomocí JavaScriptu
« Odpověď #10 kdy: 05. 01. 2022, 16:08:33 »
...
Zadání je naprosto jasné a tvoje řešení nesmyslně komplikované.

Re:Hledání a nahrazování pomocí JavaScriptu
« Odpověď #11 kdy: 05. 01. 2022, 16:09:41 »
Obecně řešení pana Jirsáka není dobře (v textu nahradím řetězec X za Y, čímž v něm vznikne řetězec Z, který je v seznamu tokenů pro další zpracování).
Nenazýval bych „není dobře“ něco, co splňuje zadání. To, že zadání nespecifikovalo chování v této situaci je chyba zadání, ne řešení :-)

Než jsem vám stihl odpovědět, že máte neúplné zadání, tak jste si našel odpověď. Zrada, proč vaše zadání nebude fungovat je nejasná definice chování replace. Ono #home je substringem #hometown a tudíž by se to nejspíš mělo zacyklit. Tak zní zadání.
Kam chodíte na to zacyklení? Když mám seznam náhrad a procházím ho od začátku do konce, nemůže dojít k zacyklení, protože dojdu na konec toho seznamu náhrad. K zacyklení by mohlo dojít tehdy, pokud bych se na konci seznamu náhrad vrátil zase na začátek a začal nahrazovat vše znova (dokud v poslední obrátce došlo k nějakému nahrazování). To je ale nesmysl. Něco takového by se dělalo jedině v případě, že by se v makra v náhradách měla zase rozvinout. Ale tam je možnost zacyklení z definice a musí si to pohlídat autor těch maker (a je to špatný návrh). A kdyby to přeci jen bylo někde potřeba, omezí se tam počet zanoření maker – pokud by se mělo celé nahrazování opakovat třeba po 11, vypíše se chyba, že je možné zanořit max. 10 úrovní maker.

Re:Hledání a nahrazování pomocí JavaScriptu
« Odpověď #12 kdy: 05. 01. 2022, 16:17:23 »
...
Zadání je naprosto jasné a tvoje řešení nesmyslně komplikované.

Ne, ze zadání nebylo jasné, čím se má nahrazovat. Dalo se chápat tak, že v textu „<a href="/user/john">@john</a >“ chce nahrazovat „@john“, „@johnathan“ a „@sarah“, ale není řečené čím – dalo by se předpokládat nějaké zobrazované jméno, přezdívka, celé jméno nebo něco takového.

Moje řešení je na pár řádek JavaScriptu. Nějak optimalizovat počet průchodů stringem nedává smysl, bavíme se o implementaci v JavaScriptu. Použití regulárních výrazů by to jen zkomplikovalo, musíte řešit escapování znaků. Navíc nic jako „hranice slova“ vám nijak nepomůže, protože nevíme nic o těch textech – jestli tam hranice slova mají nějaký význam nebo ne. Každopádně pořád je moje „komplikované“ řešení dalek olepší, než vaše neexistující řešení.

Re:Hledání a nahrazování pomocí JavaScriptu
« Odpověď #13 kdy: 05. 01. 2022, 16:30:08 »
On ale nechce homeless detekovat jako home.
Jedině v případě, že „homeless“ i „home“ jsou v seznamu zadaných tokenů. Jinak v zadání není nic o tom, že se mají nahrazovat jen celá slova, ani nic o tom, že tokeny nemohou obsahovat mezery, pomlčky, podtržítka, znaky s diakritikou…

Jak jsem psal, největší problém je správně specifikovat zadání. Řešení už pak bude snadné.

tecka

  • ***
  • 138
    • Zobrazit profil
    • E-mail
Re:Hledání a nahrazování pomocí JavaScriptu
« Odpověď #14 kdy: 06. 01. 2022, 14:52:09 »
Ze zadání vyplývá, že "plaintextové reference" na uživatele a témata chce nahradit HTML okazy.
Kód: [Vybrat]
@john and @johnathan went to see @sarah in their #hometown to look at her new #home.

@john      -->  <a href="/user/john">@john</a>
#hometown  -->  <a href="/topic/hometown">#hometown</a>
...
Je jasné, že chce nahrazovat jen "kompletní tokeny".