DaveDave
DaveDave

Reputation: 303

How to map string into object?

I have the following data string from a table and it looks like this:

The data format is correct, each row goes to a new line due to the amount of data.

const data = 
       "0 | john | doe | 
        US | Employed 
        1 | bob| dylan | 
        US | Unemployed "

How can I efficiently map data to the correct format? I want to output to an array of objects per row like below:

[{rowId: 0, name: "john", surname: "doe"}. {...}]

I initially tried to split by pipe it but it looked like this:

["0 ", " john ", " doe ", "US ", " Employed 1", " bob", " dylan ", "US ", " Unemployed"]

Upvotes: 0

Views: 901

Answers (4)

Stav Noy
Stav Noy

Reputation: 519

New answer:

If rows are split by newline after "enough" columns (5), use a regex to split:

const data =
  `0 | john | doe | 
        US | Employed 
        1 | bob| dylan | 
        US | Unemployed `;

const asObjects = data.split(/((?:[^|]+\|){4}[^|]+)(?:\n)/)
  .filter(v => v) // remove empty strings
  .map(v => v.split('|').map(v => v.trim()))
  .map(row => ({
    rowId: row[0],
    name: row[1],
    surname: row[2]
  }))
console.log(asObjects)

Explanation: string.split() will include in the result any capture groups (parenthesis) but will omit non-capturing groups (?:x)
So keep the five columns and the "|" between them - "x|x|x|x|x" - (([^|]+\|){4}[^|]+)
But ignore the inner group to avoid a duplicate: ((?:[^|]+\|){4}[^|]+)
And ignore the newline separating - add a (?:\n)


Old answer - assumption doesn't apply: Assuming correct data, with every entry is in a new line and the fields in consistent order separated by " | ":

const asObjects = data.split('\n').map(rawRow => rawRow.split(' | ')).map(row => ({ rowId: row[0], name: row[1], surname: row[2] }))

Upvotes: 0

pilchard
pilchard

Reputation: 12948

Here is an example using an external chunk() function to chunk the split data elements into the correct size arrays which are then passed to an Employee() constructor function to turn them into objects.

Initial splitting is done on \n, \r and | and the returned array trimmed and filtered for empty strings.

const
  data =
    `0 | john | doe | 
  US | Employed 
  1| bob | dylan |
  US | Unemployed `,

  chunk = (arr, chunk) => {
    let i, res = [];
    for (i = 0; i < arr.length; i += chunk) {
      res.push(arr.slice(i, i + chunk));
    }
    return res
  },

  Employee = ([rowId, name, surname, country, status]) => ({ rowId, name, surname, country, status }),

  splitData = data
    .split(/[\r\n\|]/g)               //split by newlines/returns and pipe
    .map(s => s.trim())               // trim whitespace
    .filter(s => s !== ''),           // filter out empty strings

  chunkedData = chunk(splitData, 5),  // chunk into subarrays of length

  result = chunkedData.map(Employee); // map to object using constructor declared above

console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Alternatively, given that your structure is fixed you could use a regex to capture each row, the proceed with splitting by | and object mapping.

This works regardless of whether you have line breaks or not.

const
  data1 = `0 | john | doe | 
  US | Employed 
  1| bob | dylan |
  US | Unemployed `,

  data2 = "0 | john | doe | US | Employed  1 | bob | dylan |  US | Unemployed ",

  Employee = ([rowId, name, surname, country, status]) => ({ rowId, name, surname, country, status }),

  splitData = data => data
    .match(/(?:.+?[\s\n\r]*\|[\s\n\r]*){4}.+?[\s\n\r]+/gm)   // capture each row with regex
    .map(row => row.split('|').map(s => s.trim())),         // map each row, split and trim

  result1 = splitData(data1).map(Employee), // map to object using constructor declared above
  result2 = splitData(data2).map(Employee); // map to object using constructor declared above

console.log('Multiline: \n');
console.log(result1);

console.log('\nSingle line: \n');
console.log(result2);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

PsyGik
PsyGik

Reputation: 3675

Assuming every row will have same number of columns and order will never change

// const data = `0 | john | doe | US | Employed | 1 | bob| dylan | US | Unemployed`; <= My assumption of data was incorrect.

const data = `0 | john | doe | US | Employed 1 | bob| dylan | US | Unemployed`;

const cleanedUp = data.split("|").flatMap(d => d.split(' ')).filter(d => d!== '');
console.log(cleanedUp);
const result = [];
for (var i = 0; i < cleanedUp.length; i += 5) { // i+=5 can solve your problem
  result.push({
    rowId: cleanedUp[i],
    name: cleanedUp[i + 1],
    surname: cleanedUp[i + 2]
  })
}

console.log(result)

The for loop will consider 5 elements as one row. Assuming each row starts with a numeric value like 0,1,2.. and so on

Upvotes: 1

ikhvjs
ikhvjs

Reputation: 5977

Example below

const data =
  "0 | john | doe |   US | Employed  1 | bob| dylan |  US | Unemployed ";

const arr = data
  .split("|")
  .flatMap(el => el.split(" "))
  .filter(el => el !== "");

const output = [];
for (let i = 0; i < arr.length; i += 5) {
  output.push({
    rowId: arr[i],
    name: arr[i + 1],
    surname: arr[i + 2],
    country: arr[i + 3],
    status: arr[i + 4],
  });
}

console.log(output);

Upvotes: 2

Related Questions