Creo que puedo estar cerca de una solución basada en las respuestas a esta pregunta , pero actualmente estoy un poco atascado sobre cómo abordar la diferencia de diseño del JSON con el que estoy trabajando en comparación con el ejemplo dado en esa pregunta. Este puede ser un problema más fácil de lo que pretendo; cualquier recurso que pueda orientarme en la dirección correcta sería útil.
EDITAR: El JSON con el que estoy trabajando viene de la siguiente manera:
[{"test":{"field1":"test123"},"info":"2021-10-04\nPage visit 09:57:33\n - URL: https://www.google.com/\nPage visit 09:57:50\n - URL: https://www.google.com/blah-blah-blah/\nPage visit 09:56:03\n - URL: https://google.com/random-text/blah/\n\n2021-11-04\nPage visit 13:46:03\n - URL: https://www.google.com/blah/blah-blah/\n\n"}]Mi objetivo es recorrer esta cadena, extraer cada URL, fecha y marca de tiempo, unir los valores de fecha y marca de tiempo en un solo valor de fecha y hora "yyyy-mm-dd 00:00:00" (es decir: "2021-10 -04 08:57:23"), luego envíe ese valor combinado de fecha y hora y la URL asociada a una matriz de dos columnas.
Puedo extraer la URL y los valores de fecha usando expresiones regulares, pero como un número dinámico de URL/marcas de tiempo se enumeran por separado de sus fechas relevantes, me cuesta concebir una forma de asociar las marcas de tiempo con las fechas correctas.
//Extract URLs, dates, times const urlMatches = text.match(/\bhttps?:\/\/\S+/gi); const dateMatches = text.match(/(\d{1,4}([.\-/])\d{1,2}([.\-/])\d{1,4})/g); const timeMatches = text.match(/\d{1,2}\D\d{1,2}\D(\d{4}|\d{2})/g);Primero, .split() la cadena larga por "\n" . Entonces podría usar .reduce() en la matriz dividida para manipularla de la forma que desee, como hacer coincidir, convertir a la fecha, etc.
Puede usar una combinación de .split() , .map() y .reduce() de la siguiente manera:
const jObj = jData.info.map(line => //First split @ \n\n assuming that's where the date might change line.split(/[\n]{2,}/).filter(l => l) //Then split each new element @ \n, - ,URL: .... to get date followed by time,url pairs .map(ln => ln.split(/(?:\n| - |URL: |Page visit )+/).filter(v => v)) //Manipulate the resulting data to produce timestamp,url pairs .map(arr => arr.slice(1).reduce((acc, cur, i, ar) => i % 2 === 0 ? [...acc, ar.slice(i, i + 2)] : acc, []) .map(([time, url]) => ({ timestamp: `${arr[0]} ${time}`, url })) ) //flatten the array or arrays to get an array of objects .flat() );MANIFESTACIÓN
const jData = { "info": [ "2021-10-04\nPage visit 09:57:33\n - URL: https://www.google.com/\nPage visit 09:57:50\n - URL: https://www.google.com/blah-blah-blah/\nPage visit 09:56:03\n - URL: https://google.com/random-text/blah/\n\n2021-11-04\nPage visit 13:46:03\n - URL: https://www.google.com/blah/blah-blah/\n\n" ] } const jObj = jData.info.map(line => line.split(/[\n]{2,}/).filter(l => l) .map(ln => ln.split(/(?:\n| - |URL: |Page visit )+/).filter(v => v)) .map(arr => arr.slice(1).reduce((acc,cur,i,ar) => i%2 === 0 ?[...acc,ar.slice(i,i+2)] : acc,[]) .map(([time, url]) => ({timestamp:`${arr[0]} ${time}`,url})) ) .flat() ); console.log( jObj );Puede usar map, reduce, etc., pero no sé si son más legibles que plan old forEach. Reemplace esos saltos de línea con algo. No es necesario, pero será más fácil. Luego use una expresión regular como p1 para dividirlo en grupos de fechas. Tenga en cuenta que el salto de línea doble es fácil de verificar. Use matchAll y spread para obtener eso en una matriz.
Iterar esa matriz. Debido al primer grupo de captura, la fecha está en el primer elemento. Almacene la fecha en una variable, luego use una segunda expresión regular para extraer los pares de hora/url (del segundo grupo de captura), luego itere eso en el búfer, combinándolo con el valor de fecha almacenado.
const data = { "info": [ "2021-10-04\nPage visit 09:57:33\n - URL: https://www.google.com/\nPage visit 09:57:50\n - URL: https://www.google.com/blah-blah-blah/\nPage visit 09:56:03\n - URL: https://google.com/random-text/blah/\n\n2021-11-04\nPage visit 13:46:03\n - URL: https://www.google.com/blah/blah-blah/\n\n" ] }; const text = data.info[0]; const p1 = /(\d{4}-\d{2}-\d{2})~(.*?)~~/g; let vals = [...text.replaceAll("\n", "~").matchAll(p1)]; const p2 = /page visit\s+([^~]*)~\s+-\s+url:\s+([^~]*)/ig; let buffer = []; vals.forEach(v=>{ let t = v[1]; let parsed = [...v[2].matchAll(p2)]; parsed.forEach(p=>{ buffer.push({dt: t, time: p[1], url: p[2]}); }); }); console.log(buffer); Siempre que los grupos de fechas estén separados por un doble salto de línea y la fecha misma vaya seguida de un salto de línea, entonces ese primer patrón puede ser más simple, algo como: /([^~]*)~(.*?)~~/g . Tenga en cuenta que el terminador ~~ solo funcionará si la última fecha va seguida de un salto de línea doble, pero parece que sí. Si no es así, entonces tienes que usar algo como (?:~~)? en lugar del ~~ por sí mismo.