Home > Enterprise >  In Javascript, string concat set of fields using ordering from different set of fields
In Javascript, string concat set of fields using ordering from different set of fields

Time:08-19

Difficult to title this question clearly, but we have an array of objects in Javascript that look like:

[
  {
    a1: 234,
    a2: 493,
    a3: 123,
    a1Rk: 4,
    a2Rk: 9,
    a3Rk: 6
  },{
    a1: 455,
    a2: 654,
    a3: 982,
    a1Rk: 7,
    a2Rk: 2,
    a3Rk: 2
  }
]

We'd like to string concat the fields a1, a2, a3, however the ordering of the string concats are based on the a1Rk, a2Rk, a3Rk columns. If the Rk columns are tied, tie would go to the lower property value. In the example above, we are trying to output the following:

[
  {
    a1: 234,
    a2: 493,
    a3: 123,
    a1Rk: 4,
    a2Rk: 9,
    a3Rk: 6,
    output: '234-123-493'
  },{
    a1: 455,
    a2: 654,
    a3: 982,
    a1Rk: 7,
    a2Rk: 2,
    a3Rk: 2,
    output: '654-982-455'
  }
]

In the first object, the output goes a1-a3-a2 since a1Rk < a3Rk < a2Rk. In the second object, the output goes a2-a3-a1 since a2Rk == a3Rk and 654 < 982.

CodePudding user response:

This seems to work, although we are worried about its performance over a much larger array of objects:

zed = [
  {
    a1: 234,
    a2: 493,
    a3: 123,
    a1Rk: 4,
    a2Rk: 9,
    a3Rk: 6
  },{
    a1: 455,
    a2: 654,
    a3: 982,
    a1Rk: 7,
    a2Rk: 2,
    a3Rk: 2
  }
]

let sort123 = (obj) => {
    let arr = [
        { key: 1, val: obj.a1, rk: obj.a1Rk },
        { key: 2, val: obj.a2, rk: obj.a2Rk },
        { key: 3, val: obj.a3, rk: obj.a3Rk }
    ];

    let sortedArr = arr.sort((a, b) => { if (a.rk === b.rk) { return a.val > b.val ? 1 : -1; }; return a.rk > b.rk ? 1 : -1; });
    let lineupId = '';
    sortedArr.forEach(row => lineupId = `${lineupId}-${row.val}`);
    lineupId = lineupId.slice(1);
    return lineupId;
};

let output = zed.map(row => { return { ...row, output: sort123(row) }})
console.log(output)

CodePudding user response:

const someData = [
  {
    a1: 234,
    a2: 493,
    a3: 123,
    a1Rk: 4,
    a2Rk: 9,
    a3Rk: 6
  },{
    a1: 455,
    a2: 654,
    a3: 982,
    a1Rk: 7,
    a2Rk: 2,
    a3Rk: 2
  }
];


const out = someData.map(d => {
  const order = ['a1', 'a2', 'a3'].sort((a, b) => {
    if (d[a   "Rk"] === d[b   "Rk"]) {
      return d[a] - d[b];
    } else {
      return d[a   "Rk"] - d[b   "Rk"];
    }
  });
  const output = order.map(o => d[o]).join("-");
  return {...d, output};
});

console.log(out);

CodePudding user response:

This is a four-part problem (five if you include validation):

  1. Discrimine the actual value keys from the rank keys
  2. Correlate the values at those keys
  3. Validate the correlated data (often overlooked step)
  4. Sort the correlated list by the rank values
  5. Stringify the actual values and join on a string ("-" in your expected data format)

Because of that, I think that a functional approach is appropriate, so here's one which should continue to work well (be adaptable) to varying key formats and rank suffix strings, etc.

I think the code is self-explanatory, but if you have a question then feel free to leave a comment and I'll respond.

'use strict';

function parseKey (key, suffix = 'Rk') {
  const isRank = key.endsWith(suffix);
  const name = isRank ? key.slice(0, -1 * suffix.length) : key
  return {isRank, name};
}

function correlate (obj, suffix = 'Rk') {
  const cache = {};
  for (const [key, value] of Object.entries(obj)) {
    const {isRank, name} = parseKey(key, suffix);
    const entry = (cache[name] ??= {key});
    entry[isRank ? 'rank' : 'value'] = value;
  }
  return Object.values(cache);
}

function validate (values) {
  for (const obj of values) {
    const hasExpectedValues = (
      typeof obj.rank === 'number'
      && typeof obj.value === 'number'
    );

    if (!hasExpectedValues) {
      const msg = `Matching pair not found in object: ${JSON.stringify(obj, null, 2)}`;
      throw new Error(msg);
    }
  }
  return values;
}

function sortByRank (a, b) {
  return a.rank - b.rank;
}

function getSortedString (obj, suffix = 'Rk', joinStr = '-') {
  const sorted = validate(correlate(obj, suffix)).sort(sortByRank);
  return sorted.map(({value}) => String(value)).join(joinStr);
}

const input = [
  {
    a1: 234,
    a2: 493,
    a3: 123,
    a1Rk: 4,
    a2Rk: 9,
    a3Rk: 6,
  },
  {
    a1: 455,
    a2: 654,
    a3: 982,
    a1Rk: 7,
    a2Rk: 2,
    a3Rk: 2,
  },
];

const expected = [
  {
    a1: 234,
    a2: 493,
    a3: 123,
    a1Rk: 4,
    a2Rk: 9,
    a3Rk: 6,
    output: '234-123-493',
  },
  {
    a1: 455,
    a2: 654,
    a3: 982,
    a1Rk: 7,
    a2Rk: 2,
    a3Rk: 2,
    output: '654-982-455',
  },
];

const actual = input.map(obj => ({
  ...obj,
  output: getSortedString(obj),
}));

console.log(JSON.stringify(actual) === JSON.stringify(expected)); // true

  • Related