casillas
casillas

Reputation: 16793

JavaScript: Convert array of objects into hashmap in Generic

I have a set of values in an array where each value has an ID and LABEL.

const data = [
{ID: 0, LABEL: 'turbo'},
{ID: 1, LABEL: 'classic'},
{ID: 7, LABEL: 'unknown'}
];

const hashMap = data.reduce((result, item) => {
  return { ...result, [ item.ID ] : item.LABEL };
}, {});

const hashMapJson = JSON.stringify(hashMap);

console.log('hashMap', hashMap);
console.log('hashMapJson', hashMapJson);

But if I have the following json object where key is slightly different, it does not handle of course. I wonder how I could able to make the above solution in more generic to tackle different inputs.

const data = [
{K_ID: 0, K_LABEL: 'turbo'},
{L_ID: 1, K_LABEL: 'classic'},
{S_ID: 7, K_LABEL: 'unknown'}
];

Upvotes: 1

Views: 310

Answers (5)

junvar
junvar

Reputation: 11574

You can make create an extractIdLabel function that tries to guess the keys for ID and Label respectively. You can use regex (via string.prototype.match) or other tools to make it as robust as required.

In the example below,

1) it looks for the keys 'id' and 'label'. e.g. {id: 0, label: 'zero'}

2) If one or both are not found, then it looks for keys containing the strings 'id' and 'label'. e.g. {x_id: 0, y_label_z: 'zero'}

3) If still one of them is not found, but the other is found, and the item has 2 keys, it uses the unused key for the not-found one. e.g. {id: 0, obviously_not_id_so_must_be_L_a_b_e_l: 'zero'}

const data = [
  {K_ID: -1, K_LABEL: 'turbo'},
  {L_ID: 1, K_LABEL: 'classic'},
  {S_ID: 7, K_LABEL: 'unknown'},
  {ID: 10, LABEL: 'ten', garbage: 'garbage', bad_label: 'bad_label'},
  {KEY: 11, X_LABEL: 'eleven'},
  {Y_ID: 12, VALUE: 'twelve'},
  {id: 0, obviously_not_id_so_must_be_L_a_b_e_l: 'zero'}
];

let extractIdLabel = item => {
  let keys = Object.keys(item);
  let id = keys.find(key => key.toLowerCase() === 'id') ||
      keys.find(key => key.toLowerCase().includes('id'));
  let label = keys.find(key => key.toLowerCase() === 'label') ||
      keys.find(key => key.toLowerCase().includes('label'));
  if (keys.length === 2 && !id ^ !label)
    if (!id)
      id = keys.find(key => key !== label);
    else
      label = keys.find(key => key !== id);
  return [item[id], item[label]];
};

const hashMap = data.reduce((result, item) => {
  let [id, label] = extractIdLabel(item);
  return {...result, [id]: label};
}, {});

const hashMapJson = JSON.stringify(hashMap);

console.log('hashMap', hashMap);
console.log('hashMapJson', hashMapJson);

Upvotes: 1

Nikhil Aggarwal
Nikhil Aggarwal

Reputation: 28445

You can try following using String.endsWith

const data = [{K_ID: 0, K_LABEL: 'turbo'},{L_ID: 1, K_LABEL: 'classic'},{S_ID: 7, K_LABEL: 'unknown'}];

const hashMap = data.reduce((result, item) => {
  const temp = Object.keys(item);
  const key = temp[0].endsWith("_ID") ? 0 : 1; // check for 1st key ending with _ID
  const value = +!key; // if key is 1, value be 0 and other way around
  result[item[temp[key]]] = item[temp[value]];
  return result;
}, {});

const hashMapJson = JSON.stringify(hashMap);
console.log(hashMapJson);

Upvotes: 2

Jonathan Beadle
Jonathan Beadle

Reputation: 418

You could change your hashMap function to something like the following :

const data = [{K_ID: 0, K_LABEL: 'turbo'},{L_ID: 1, K_LABEL: 'classic'},{S_ID: 7, K_LABEL: 'unknown'}];

const hashMap = data.reduce((result, item) => {
    let id
    let label

    Object.keys(item).forEach((key) => {
        if (key.includes('ID')) {
            id = key
        } else if (key.includes('LABEL')) {
            label = key
        }
    })

    return { ...result, [ item[id] ] : item[label] };
}, {});

console.log( hashMap );

This is assuming your data set you are passing (data) is similar the the one in the example you provided. As long as a key contains ID, it'll make that the id key and as long as a key contains LABEL it will make that the key for the LABEL

Upvotes: 2

Nina Scholz
Nina Scholz

Reputation: 386522

You could take a function for finding a similar key of the object.

const 
    getSimilarKey = (object, key) => Object.keys(object).find(k => k.toLowerCase().includes(key.toLowerCase())),
    data = [{ K_ID: 0, K_LABEL: 'turbo' }, { L_ID: 1, K_LABEL: 'classic' }, { S_ID: 7, K_LABEL: 'unknown' }],
    key = 'id', 
    value = 'label',
    result = Object.assign(
        {},
        ...data.map(o => ({ [o[getSimilarKey(o, key)]]: o[getSimilarKey(o, value)] }))
    );

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

With upcoming Object.fromEntries

const 
    getSimilarKey = (object, key) => Object.keys(object).find(k => k.toLowerCase().includes(key.toLowerCase())),
    data = [{ K_ID: 0, K_LABEL: 'turbo' }, { L_ID: 1, K_LABEL: 'classic' }, { S_ID: 7, K_LABEL: 'unknown' }],
    key = 'id', 
    value = 'label',
    result = Object.fromEntries(data.map(o => ({ [o[getSimilarKey(o, key)]]: o[getSimilarKey(o, value)] })));

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

Upvotes: 2

BJRINT
BJRINT

Reputation: 667

It really depends of your data model, but this should work...

const data = [
{K_ID: 0, K_LABEL: 'turbo'},
{L_ID: 1, K_LABEL: 'classic'},
{S_ID: 7, K_LABEL: 'unknown'}
]

const hashMap = data.reduce((ac, cv) => {
  const keys = Object.keys(cv)
  let id, label
  keys.forEach(key => {
	if(key.endsWith('_LABEL'))
		label = cv[key]
	else if(key.endsWith('_ID'))
		id = cv[key]
  })
  
  if(id !== undefined && label !== undefined)
	ac[id] = label
  
  return ac
}, {})

console.log(hashMap)

Upvotes: 2

Related Questions