Convert array of employees to Dictionary

780 Views Asked by At

So I'm using the LinqJS library to currently achieve the following:

    var allEmployees = [
        {
            "Id": 1374,
            "FirstName": "John",
            "LastName": "Doe"
        },
        {
            "Id": 1375,
            "FirstName": "Jane",
            "LastName": "Doe"
        }
    ];
    var employeeDictionary = Enumerable.from(allEmployees).toDictionary("$.Id", "$.FirstName+' '+$.LastName").toEnumerable().toArray();

When serializing employeeDictionary to JSON, I get the following format:

[
  {
    "key": 1374,
    "value": "John Doe"
  },
  {
    "key": 1375,
    "value": "Jane Doe"
  }
]

I do not want my data in this format, I wish to have it in this format:

{
  "1374": "John Doe",
  "1375": "Jane Doe"
}

I wrote something similar in PHP using YaLinqo which gives me the results I need:

echo json_encode(from($employees)->toDictionary('$v["Id"]', '$v["FirstName"]." ".$v["LastName"]')->toArray());

However, I would like to be able to achieve this in my JavaScript.

4

There are 4 best solutions below

5
VLAZ On BEST ANSWER

In JavaScript, an array is strictly always numerically indexed structure. So, .toArray abides with that. In PHP an array is closer to what JavaScript considers plain objects.


If using this LINQ JavaScript library

You can use the .toObject method to produce an object of your format - you need to pass in two functions - a key selector and a value selector, so the object gets built with the correct data:

var allEmployees = [
    {
        "Id": 1374,
        "FirstName": "John",
        "LastName": "Doe"
    },
    {
        "Id": 1375,
        "FirstName": "Jane",
        "LastName": "Doe"
    }
];
var employeeDictionary = Enumerable.from(allEmployees)
  .toDictionary("$.Id", "$.FirstName+' '+$.LastName")
  .toEnumerable()
  .toObject(entry => entry.key, entry => entry.value);

/* output:
{
  "1374": "John Doe",
  "1375": "Jane Doe"
}
*/

Using destructuring, the key/value selectors can be transformed to:

.toObject(({key}) => key, ({value}) => value);

If using this library for LINQ operations, then you need to slightly change the syntax:

var allEmployees = [
    {
        "Id": 1374,
        "FirstName": "John",
        "LastName": "Doe"
    },
    {
        "Id": 1375,
        "FirstName": "Jane",
        "LastName": "Doe"
    }
];
var employeeDictionary = Enumerable.From(allEmployees)
  .ToDictionary("$.Id", "$.FirstName+' '+$.LastName")
  .ToEnumerable()
  .ToObject("$.Key", "$.Value");

/* output:
{
  "1374": "John Doe",
  "1375": "Jane Doe"
}
*/
0
Clarity On

You can use reduce to achieve this:

employeeDictionary.reduce((acc, employee) => {
  acc[employee.key] = employee.value;
  return acc
}, {})

Or with a shorter version by using object spread:

employeeDictionary.reduce((acc, employee) => ({
  ...acc,
  [employee.key]: employee.value
}), {})
0
Mr. Polywhirl On

I think you are confusing an array of objects with a dictionary. I included both versions below to show the difference.

What you want to do with the data is map it, instead of reducing it.

let allEmployees = [
  { "Id": 1374, "FirstName": "John", "LastName": "Doe" },
  { "Id": 1375, "FirstName": "Jane", "LastName": "Doe" }
];

let defaultOptions = {
  keyField: 'Id',
  valueFn: (emp) => `${emp['FirstName']} ${emp['LastName']}`
};

console.log('Dictionary', toDictionary(allEmployees, defaultOptions));
console.log('Pairs', toPairs(allEmployees, defaultOptions));

function toDictionary(list, options) {
  let opts = Object.assign({ keyField: 'key', valueField: 'value' }, options);
  return list.reduce((dict, item) => (Object.assign(dict, {
    [item[opts.keyField]] : opts.valueFn ? opts.valueFn(item) : item[opts.valueField]
  })), {});
}

function toPairs(list, options) {
  let opts = Object.assign({ keyField: 'key', valueField: 'value' }, options);
  return list.map((item) => ({
    key   : item[opts.keyField],
    value :  opts.valueFn ? opts.valueFn(item) : item[opts.valueField]
  }));
}
.as-console-wrapper { top: 0; max-height: 100% !important; }


Here is a list wrapper class.

class ListWrapper {
  constructor(list, options) {
    this.list       = list;
    this.keyField   = options.keyField   || 'key'
    this.valueField = options.valueField || 'value'
    this.valueFn    = options.valueFn               /* optional */
  }
  toPairs() {
    return this.list.map(e=>({key:e[this.keyField],value:this.valueFn?this.valueFn(e):e[this.valueField]}));
  }
  toDictionary() {
    return this.list.reduce((d,e)=>(Object.assign(d,{[e[this.keyField]]:this.valueFn?this.valueFn(e):e[this.valueField]})),{});
  }
}

let allEmployees = [
  { "Id": 1374, "FirstName": "John", "LastName": "Doe" },
  { "Id": 1375, "FirstName": "Jane", "LastName": "Doe" }
];

let listWrapper = new ListWrapper(allEmployees, {
  keyField: 'Id',
  valueFn: (emp) => `${emp['FirstName']} ${emp['LastName']}`
});

console.log('Dictionary', listWrapper.toDictionary());
console.log('Pairs', listWrapper.toPairs());
.as-console-wrapper { top: 0; max-height: 100% !important; }

0
Nina Scholz On

A short linq version

var allEmployees = [{ Id: 1374, FirstName: "John", LastName: "Doe" }, { Id: 1375, FirstName: "Jane", LastName: "Doe" }],
    employeeDictionary = Enumerable.From(allEmployees)
        .Select("$ => { key: $.Id, value: $.FirstName + ' ' + $.LastName }")
        .ToObject("$.key", "$.value");
    
console.log(employeeDictionary);
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>

A short approach with a destructuring and Object.fromEntries.

var allEmployees = [{ Id: 1374, FirstName: "John", LastName: "Doe" }, { Id: 1375, FirstName: "Jane", LastName: "Doe" }],
    employeeDictionary = Object.fromEntries(
        allEmployees.map(({ Id, FirstName, LastName }) =>
            [Id, [FirstName, LastName].join(' ')])
    );
    
console.log(employeeDictionary);