Katzhuu
Katzhuu

Reputation: 162

Populating array from a repeating array

I'm making a small tool for handling translations for a website. I already got this code working, but I feel there should be some more elegant and readable way using array methods (mine looks like a mess...).

Basically, I'll get input in the format shown in code (data_import, this is just fake data for testing). It has 4 columns [translationTag, uniqueId, languageId, translation]. Order of rows is same for every language and there is same number of rows for each language. Number of languages may change from 2 upwards.

the desired output would be like this:

const data_import =  [

  ['aaa', {id:1, langId:1, finnish:'tuntematon'}, {id:5, langId:4, english:'unknown'}, {id:9, langId:6, swedish:'okänd'}],
  ['bbb', {id:2, langId:1, finnish:'auto'}, {id:6, langId:4, english:'car'}, {id:10, langId:6, swedish:'bil'}],
  ['ccc', {id:3, langId:1, finnish:'polkupyörä'}, {id:7, langId:4, english:'bicycle'}, {id:11, langId:6, swedish:'cykel'}],
  ['ddd', {id:4, langId:1, finnish:'rullalauta'}, , {id:8, langId:4, english:'skateboard'}, {id:12, langId:6, swedish:'skateboard'}]
];

Here is my code that 'works' but is ugly and unreadable...

export const language = ['Finnish', 'Estonia', 'Polish', 'English', 'Spanish', 'Swedish'];

const data_import =  [
  ['aaa', 1, 1, 'tuntematon'],
  ['bbb', 2, 1, 'auto'],
  ['ccc', 3, 1, 'polkupyörä'],
  ['ddd', 4, 1, 'rullalauta'],
  ['aaa', 5, 4, 'unknown'],
  ['bbb', 6, 4, 'car'],
  ['ccc', 7, 4, 'bicycle'],
  ['ddd', 8, 4, 'skateboard'],
  ['aaa', 9, 6, 'okänd'],
  ['bbb', 10, 6, 'bil'],
  ['ccc', 11, 6, 'cykel'],
  ['ddd', 12, 6, 'skateboard']];

export const data = process_test(data_import);

function process_test(data) {  
  const numberOfCols = data[0].length;
  const idIndex = numberOfCols - 2;
  const arr_result = []
  let rowMax = 0;
  let rowMaxMulti = 0;
  let langIdLast = 0;
  data.forEach((row, index) => {
    // if = add non-language cols and first language column
    if(row[idIndex] === data[0][idIndex]) {
      rowMax = index + 1;
      const transItem = row.slice(0, idIndex-1);
      transItem.push({ id:row[idIndex], langId:row[idIndex], [language[row[idIndex] - 1]]:row[idIndex + 1] });
      arr_result[index] = transItem;
      langIdLast = row[idIndex];
    }
    // add other languages to datarow
    else {
      const transItem = { id:row[idIndex - 1], langId:row[idIndex], [language[row[idIndex] + 1]]:row[idIndex + 1] };
      if(langIdLast !== row[idIndex]) rowMaxMulti++;
      arr_result[index - rowMax * rowMaxMulti].push(transItem);
      langIdLast = row[idIndex];
    }
  })
  return(arr_result);
}

Upvotes: 3

Views: 65

Answers (3)

Leonid Pyrlia
Leonid Pyrlia

Reputation: 1712

You can group the data by tag with Array.prototype.reduce and map it out to the desired format with Object.keys

const language = ['Finnish', 'Estonia', 'Polish', 'English', 'Spanish', 'Swedish'];

const data_import = [['aaa', 1, 1, 'tuntematon'],['bbb', 2, 1, 'auto'],['ccc', 3, 1, 'polkupyörä'],['ddd', 4, 1, 'rullalauta'],['aaa', 5, 4, 'unknown'],['bbb', 6, 4, 'car'],['ccc', 7, 4, 'bicycle'],['ddd', 8, 4, 'skateboard'],['aaa', 9, 6, 'okänd'],['bbb', 10, 6, 'bil'],['ccc', 11, 6, 'cykel'],['ddd', 12, 6, 'skateboard']];

const grouped = data_import.reduce((all, [tag, id, langId, tran]) => {
  
  if (!all.hasOwnProperty(tag)) all[tag] = [];

  all[tag].push({id, langId, [language[langId-1]]: tran});

  return all;

}, {});

const result = Object.keys(grouped).map(tag => [tag, ...grouped[tag]]);

console.log(result);

Upvotes: 1

Narendra Jadhav
Narendra Jadhav

Reputation: 10262

You could also use

  1. Array.prototype.map()
  2. Array.prototype.filter()
  3. new Set([])
  4. destructuring assignment

const data_import = [["aaa",1,1,"tuntematon"],["aaa",5,4,"unknown"],["aaa",9,6,"okänd"],["bbb",6,4,"car"],["bbb",2,1,"auto"],["bbb",10,6,"bil"],["ccc",11,6,"cykel"],["ccc",7,4,"bicycle"],["ccc",3,1,"polkupyörä"],["ddd",8,4,"skateboard"],["ddd",4,1,"rullalauta"],["ddd",12,6,"skateboard"]],

      language = ['Finnish', 'Estonia', 'Polish', 'English', 'Spanish', 'Swedish'];

const keys =[...new Set(data_import.map(v => v[0]))];

let result = keys.map(key => [key, data_import.filter(v => v[0] == key).map(v => {
    return {
        id: v[1],
        langId: v[2],
        [language[v[2]-1]]: v[3]
    }
})]);

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

Upvotes: 0

CertainPerformance
CertainPerformance

Reputation: 371019

It would be simpler to reduce into an object indexed by the translation tag, and then get that object's values. On each iteration, create an array for the translation tag if it doesn't already exist in the accumulator. Identify the language name from the langId of the current item, and push the new object to the array:

const language = ['Finnish', 'Estonia', 'Polish', 'English', 'Spanish', 'Swedish'];

const data_import =  [
  ['aaa', 1, 1, 'tuntematon'],
  ['bbb', 2, 1, 'auto'],
  ['ccc', 3, 1, 'polkupyörä'],
  ['ddd', 4, 1, 'rullalauta'],
  ['aaa', 5, 4, 'unknown'],
  ['bbb', 6, 4, 'car'],
  ['ccc', 7, 4, 'bicycle'],
  ['ddd', 8, 4, 'skateboard'],
  ['aaa', 9, 6, 'okänd'],
  ['bbb', 10, 6, 'bil'],
  ['ccc', 11, 6, 'cykel'],
  ['ddd', 12, 6, 'skateboard']];

const data = Object.values(data_import.reduce((a, [tTag, id, langId, word]) => {
  if (!a[tTag]) a[tTag] = [tTag];
  const langName = language[langId - 1];
  a[tTag].push({ id, langId, [langName]: word });
  return a;
}, {}));
console.log(data);

Upvotes: 2

Related Questions