Mapping array of objects to array of arrays with headers using ES6 - javascript

I want to map an array of objects, which I got from a json response of my backend, to an array of arrays, with the first row being an array of headers (titles). I'll use this array to make it downloadable in a csv file.
Also, I want to keep away a couple of headers / columns that are not really interesting for the end user to have in their csv file.
My code is working fine, but I have the idea that it can be done with more concise code. I'm fine with using ES6 / ES2015, but not really experienced with spread syntax and other ES6 goodies myself, so any suggestions for a better, more modern (functional / reactive?) approach are greatly appreciated.
const originalData = [
{name: 'Gizmo', species: 'cat', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping'},
{name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail'},
{name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing'}
]
let headers = []
const firstRow = originalData[0]
for (var key in firstRow) {
if (firstRow.hasOwnProperty(key)) {
if (!['raw','updated_at'].includes(key)) {
headers.push(key)
}
}
}
const d = originalData.map(function(_, i) {
return headers.map(function(header) {
return originalData[i][header]
}.bind(this))
}.bind(this))
const result = [headers].concat(d)
console.log(result)

Something like this?
const originalData = [
{ name: 'Gizmo', species: 'cat', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping' },
{ name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail' },
{ name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing' }
]
const headers = Object.keys(originalData[0]).filter(key => !['raw', 'updated_at'].includes(key));
const d = originalData.map(obj => headers.map(key => obj[key]))
const result = [headers, ...d];
console.log(result)

Basically you could use a closure over the filtered keys and map and concat the arrays.
const fn = (array => (keys => [keys].concat(array.map(o => keys.map(k => o[k]))))
(Object.keys(array[0]).filter(k => !['raw','updated_at'].includes(k)))),
data = [{ name: 'Gizmo', species: 'cat', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping' }, { name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail' }, { name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing' }],
result = fn(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

const originalData = [
{name: 'Gizmo', species: 'cat', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping'},
{name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail'},
{name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing'}
];
const propertiesNeeded = Object.keys(originalData[0]).filter(prop => !['raw', 'updated_at'].includes(prop));
const dataMapped = originalData.map(obj => propertiesNeeded.map(prop => obj[prop]));
const finalArr = [propertiesNeeded, ...dataMapped];

This is how I'd do it. I figured if you know which keys you're after then we can make good use of that.
const data = [
{name: 'Gizmo', species: 'cat', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping'},
{name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail'},
{name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing'}
]
const desiredKeys = ['name', 'species', 'age', 'skill']
const result = [desiredKeys].concat(data.map(pet => desiredKeys.map(key => pet[key])))
console.log(result)

Yours is good. You could simplify the header creation by using Object.keys
const originalData = [
{name: 'Gizmo', species: 'cat', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping'},
{name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail'},
{name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing'}
]
const headers = Object.keys(originalData[0])
.filter(key => !['raw','updated_at'].includes(key)));
const data = originalData.map(row => headers.map(header => row[header]));
console.log(headers, data);

Try with Array#map used for recreate the array with Object.key and value . and new Set() method user for create the key set value .ignore the repeated one ... its spread syntax
const originalData = [
{name: 'Gizmo', species: 'cat', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping'},
{name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail'},
{name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing'}
]
var result = [[...new Set(...originalData.map(a=> Object.keys(a)))]].concat(originalData.map(a=> Object.values(a)))
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }

A single line of code in an unbroken chain using a filter and reduce.
var unborken = chain => chain.filter((_, i, xx) =>
delete xx[i].updated_at && delete xx[i].raw).reduce((aac, _, i, aa) =>
(i === 0 ? aac.push(Object.keys(aa[i])) && aac.push(Object.values(aa[i])) :
aac.push(Object.values(aa[i])), aac), []);
const originalData = [{
name: 'Gizmo',
species: 'cat',
age: '9',
raw: 'G9e76rd',
updated_at: '1318874398806',
skill: 'sleeping'
},
{
name: 'Benny',
species: 'dog',
age: '3',
raw: '98HDo2h',
updated_at: '1318874392417',
skill: 'chasing tail'
},
{
name: 'Oscar',
species: 'cat',
age: '2',
raw: '9da8Ro1',
updated_at: '1318874390283',
skill: 'meowing'
}
];
console.log(unborken(originalData));
.as-console-wrapper { max-height: 100% !important; top: 0; }

I'd like to point out that an object's keys order is not entirely "fixed" by spec.
If your first animal in your originalData starts with a species property, your whole table will be formatted in that column order...
Therefore I'd advice you to explicitly define your columns in an array, in which order does matter.
Note that in the example below I've swapped the property declaration order of Gizmo. Put this data in your own code and the first column will be species. (at least in my browser it is, I guess it can even differ between browsers?)
const data = [
{species: 'cat', name: 'Gizmo', age: '9', raw: 'G9e76rd', updated_at: '1318874398806', skill: 'sleeping'},
{name: 'Benny', species: 'dog', age: '3', raw: '98HDo2h', updated_at: '1318874392417', skill: 'chasing tail'},
{name: 'Oscar', species: 'cat', age: '2', raw: '9da8Ro1', updated_at: '1318874390283', skill: 'meowing'}
]
const getProps = props => obj =>
props.map(k => obj[k]);
const columns = ["name", "species", "age", "skill"];
console.log(
[columns, ...data.map(getProps(columns))]
);

Related

How to compare the sameness of object entries of 2 arrays and how to create a merger of both objects with custom properties of the found same object?

I am trying to compare 2 objects by their property and the values Strictly using forloop. If the value of the "name" or another property matches up with each other, I want to push the property and value to value3.
Once value3 is logged, I want the response like this:
{
name: 'dog',
surname: 'good',
skills: 'programming',
age: '22'
},
{
name: 'cat',
surname: 'soft',
skills: 'engineer'
age: '12'
},
{
name: 'elephant',
surname: 'big',
skills: 'programming'
age: '23'
}
Here is the code:
var values1 = [
{
name: 'dog',
surname: 'good',
skills: 'programming'
},
{
name: 'cat',
surname: 'soft',
skills: 'engineer'
},
{
name: 'elephant',
surname: 'big',
skills: 'programming'
}
]
var values2 = [
{
name: 'cat',
food: 'fish',
age: '12'
},
{
name: 'elephant',
food: 'leafs',
age: '13'
},
{
lastname: 'dog',
food: 'treats',
age: '22'
}
]
// push into this empty object array
var values3 = [{}]
console.log(values3)
The most generic approach which fulfills all of the OP's requirements should be based on Array.prototype.reduce. Its advantage comes with utilizing the additionally passed optional initial value as kind of configurable collector/accumulator object which will carry all the needed additional information / functions / result. Thus one can provide a reusable function with a customizable context/environment which one can adapt to ones needs.
var values1 = [{
name: 'dog',
surname: 'good',
skills: 'programming',
}, {
name: 'cat',
surname: 'soft',
skills: 'engineer',
}, {
name: 'elephant',
surname: 'big',
skills: 'programming',
}];
var values2 = [{
name: 'cat',
food: 'fish',
age: '12'
}, {
name: 'elephant',
food: 'leafs',
age: '13'
}, {
lastname: 'dog',
food: 'treats',
age: '22'
}];
function mergeItemsOfSameEntry(collector, item) {
const {
getSameEntryValue,
getMergeSubType,
comparisonItems,
result
} = collector;
const itemValue = getSameEntryValue(item);
const comparisonItem = comparisonItems
.find(cItem => getSameEntryValue(cItem) === itemValue);
if (comparisonItem !== null) {
result.push({
...item,
...getMergeSubType(comparisonItem),
});
}
return collector;
}
const values3 = values1.reduce(mergeItemsOfSameEntry, {
getSameEntryValue: item => item.name ?? item.lastname,
getMergeSubType: ({ age }) => ({ age }),
comparisonItems: values2,
result: [],
}).result;
console.log({ values3 });
.as-console-wrapper { min-height: 100%!important; top: 0; }
If you just want the key and value pair, you can do something like this:
var values1 = [
{
name: 'dog',
surname: 'good',
skills: 'programming'
},
{
name: 'cat',
surname: 'soft',
skills: 'engineer'
},
{
name: 'elephant',
surname: 'big',
skills: 'programming'
}
]
var values2 = [
{
name: 'cat',
food: 'fish',
age: '12'
},
{
name: 'elephant',
food: 'leafs',
age: '13'
},
{
lastname: 'dog',
food: 'treats',
age: '22'
}
]
// push into this empty object array
var values3 = [];
values1.forEach(eachValue1Obj => {
const keys = Object.keys(eachValue1Obj);
keys.forEach(eachKey => {
values2.forEach(eachValue2Obj => {
if (
eachValue1Obj[eachKey] &&
eachValue2Obj[eachKey] &&
eachValue1Obj[eachKey] === eachValue2Obj[eachKey]
) {
const x = {
key: eachKey,
value: eachValue1Obj[eachKey]
};
values3.push(x)
}
})
})
})
console.log('Values 3 Array ==>', values3);

How to compare the property of object entries of 2 length arrays and merge into one? [duplicate]

This question already has answers here:
Filter array of objects with another array of objects
(11 answers)
Closed 1 year ago.
I am trying to compare values1 and values2 by their property and the values. If the value of the name property matches up with each other, I want to push the property and value to value3.
But I am getting this error: Cannot read property 'age' of undefined
const values1 = [
{ name: 'dog', surname: 'good', skills: 'programming' },
{ name: 'cat', surname: 'soft', skills: 'engineer' },
{ name: 'elephant', surname: 'big', skills: 'programming' }
]
const values2 = [
{ name: 'cat', age: '12' },
{ name: 'elephant', age: '13' },
]
const values3 = values1.map((value1) => {
return Object.assign(value1, { age: values2.filter(value2 => value2.name === value1.name)[0].age })
})
console.log(values3)
This is the result I would like to return.
{
name: 'cat',
surname: 'soft',
skills: 'engineer'
age: '12'
},
{
name: 'dog',
surname: 'good',
skills: 'programming',
},
{
name: 'elephant',
surname: 'big',
skills: 'programming'
age: '23'
}
You can do it like this:
const values1 = [{
name: 'dog',
surname: 'good',
skills: 'programming'
},
{
name: 'cat',
surname: 'soft',
skills: 'engineer'
},
{
name: 'elephant',
surname: 'big',
skills: 'programming'
}
]
const values2 = [{
name: 'cat',
age: '12'
},
{
name: 'elephant',
age: '13'
},
]
const values3 = values1.map((value1) => {
const sameObj = values2.find(value2 => value2.name === value1.name);
if (sameObj) {
return { ...value1, age: sameObj.age }
}
return value1;
});
console.log(values3)

Comparing 2 objects' values and push the values to one object array

I am trying to compare 2 objects by their property and the values. If the value of the "name" property matches up with each other, I want to push the property and value to value3.
Once value3 is logged, I want the response like this:
{
name: 'dog',
surname: 'good',
skills: 'programming',
age: '22'
},
{
name: 'cat',
surname: 'soft',
skills: 'engineer'
age: '12'
},
{
name: 'elephant',
surname: 'big',
skills: 'programming'
age: '23'
}
Here is the code:
var values1 = [
{
name: 'dog',
surname: 'good',
skills: 'programming'
},
{
name: 'cat',
surname: 'soft',
skills: 'engineer'
},
{
name: 'elephant',
surname: 'big',
skills: 'programming'
}
]
var values2 = [
{
name: 'cat',
food: 'fish',
age: '12'
},
{
name: 'elephant',
food: 'leafs',
age: '13'
},
{
name: 'dog',
food: 'treats',
age: '22'
}
]
// push into this empty object array
var values3 = [{}]
console.log(values3)
const values1 = [
{ name: 'dog', surname: 'good', skills: 'programming' },
{ name: 'cat', surname: 'soft', skills: 'engineer' },
{ name: 'elephant', surname: 'big', skills: 'programming' }
]
const values2 = [
{ name: 'cat', food: 'fish', age: '12' },
{ name: 'elephant', food: 'leafs', age: '13' },
{ name: 'dog', food: 'treats', age: '22' }
]
const values3 = values1.map((value1) => {
return Object.assign(value1, { age: values2.filter(value2 => value2.name === value1.name)[0].age })
})
console.log(values3)
The code above will only work if for each name in values1 an object with name exists in values2. If not use this code:
const values3 = values1.map((value1) => {
const found = values2.find(value2 => value2.name === value1.name)
return Object.assign(value1, { age: found ? found.age : undefined })
})

How can I access a specific object in my array using JavaScript?

I have created a dynamic array of objects which is created through inquirer.
But I cannot figure out how to access a specific object in the array
EDIT: this is how the console has logged my array
So for example, how can I access the 2nd Engineer (Mark)?
Keep in mind the array will change depending on the user input
team = [
Manager {
name: 'Nicole',
id: '1',
email: 'nicole#gmail.com',
officeNumber: '5'
},
Engineer {
name: 'Zoe',
id: '2',
email: 'zoe#gmail.com',
github: 'zozo'
},
Engineer {
name: 'Mark',
id: '3',
email: 'mark#gmail.com',
github: 'emman'
},
Engineer {
name: 'Joe',
id: '4',
email: 'joe#gmail.com',
github: 'joey'
}
Intern {
name: 'Seb',
id: '5',
email: 'seb#gmail.com',
school: 'UWA'
}
]
Use find method. If there is no such Mark then find return null.
If you want find Engineer Mark
const result = data.find(x => {
return x instanceof Engineer && x.name === 'Mark'
})
[Update]
If you want find the second Engineer
const result = data.filter(x => {
return x instanceof Engineer
})[1]
As Sepehr jozef mentioned the strucure is not that handy. If we take his structure you can find it via the .find Method.
var team = [
{
name: 'Nicole',
id: '1',
email: 'nicole#gmail.com',
officeNumber: '5',
},
{
name: 'Zoe',
id: '2',
email: 'zoe#gmail.com',
github: 'zozo'
},
{
name: 'Mark',
id: '3',
email: 'mark#gmail.com',
github: 'emman'
},
{
name: 'Joe',
id: '4',
email: 'joe#gmail.com',
github: 'joey'
},
{
name: 'Seb',
id: '5',
email: 'seb#gmail.com',
school: 'UWA'
}
]
const mark = team.find(function(teamMember){
return teamMember.name === "Mark";
})
The variable "mark" contains now the object of the engineer "Mark".
first of all, your structure is wrong.
it should be:
var team = [
{
name: 'Nicole',
id: '1',
email: 'nicole#gmail.com',
officeNumber: '5',
},
{
name: 'Zoe',
id: '2',
email: 'zoe#gmail.com',
github: 'zozo'
},
{
name: 'Mark',
id: '3',
email: 'mark#gmail.com',
github: 'emman'
},
{
name: 'Joe',
id: '4',
email: 'joe#gmail.com',
github: 'joey'
},
{
name: 'Seb',
id: '5',
email: 'seb#gmail.com',
school: 'UWA'
}
]
and to get mark(2) you should use:
team[3].name

How do I compare two arrays of objects to see if they have the same ids and then return another value from the matching object?

So here are two sample arrays:
const sample1 = [{id: '1', name: 'jon', quantity: '5'}, {id: '2', name: 'sue', quantity: '4'}, {id: '3', name: 'greg', quantity: '7'}];
const sample2 = [{id: '1', dish: 'cheesecake'}, {id: '2', dish: 'carrot cake'}, {id: '3', dish: 'cupcake'}];
I would like to write a function to compare the ids from sample1 with the ids of sample2. If the ids match I need to return the dish value from sample2. I tried mapping sample1 and using find() inside the loop on sample2 but could not get it to work.
You could take a Set, filter the objects and map the wanted property.
const
sample1 = [{ id: '1', name: 'jon', quantity: '5' }, { id: '2', name: 'sue', quantity: '4' }, { id: '3', name: 'greg', quantity: '7' }],
sample2 = [{ id: '1', dish: 'cheesecake' }, { id: '2', dish: 'carrot cake' }, { id: '3', dish: 'cupcake' }],
set1 = new Set(sample1.map(({ id }) => id)),
result = sample2
.filter(({ id }) => set1.has(id))
.map(({ dish }) => dish);
console.log(result);
If you want all sample2 dishes that have a matching id in sample1, the following code does the trick.
const sample1 = [{id: '1', name: 'jon', quantity: '5'}, {id: '2', name: 'sue', quantity: '4'}, {id: '3', name: 'greg', quantity: '7'}];
const sample2 = [{id: '1', dish: 'cheesecake'}, {id: '2', dish: 'carrot cake'}, {id: '3', dish: 'cupcake'}];
// Array with all Sample2 dishes that have a matching id in Sample1
const result = sample2
.filter(sample2Entry => sample1.some(sample1Entry => sample1Entry.id === sample2Entry.id))
.map(sample2Entry => sample2Entry.dish);
If u want to combine the two based on the id you can do it like this:
const sample1 = [{id: '1', name: 'jon', quantity: '5'}, {id: '2', name: 'sue', quantity: '4'}, {id: '3', name: 'greg', quantity: '7'}];
const sample2 = [{id: '1', dish: 'cheesecake'}, {id: '2', dish: 'carrot cake'}, {id: '3', dish: 'cupcake'}];
const result = sample1.map(s1item => ({
...s1item,
dish: sample2.find(s2item => s2item.id === s1item.id) ? sample2.find(s2item => s2item.id === s1item.id).dish : null
}))
console.log(result)
You could firstly .map() your sample1 array to an array of ids. You can then build a look up table from your sample2 array which associates an array of dishes with an id key. Then using the array you made initially using .map(), you can .flatMap() over that array to get all your dishes for each given user:
const sample1 = [{id: '1', name: 'jon', quantity: '5'}, {id: '2', name: 'sue', quantity: '4'}, {id: '3', name: 'greg', quantity: '7'}];
const sample2 = [{id: '1', dish: 'cheesecake'}, {id: '2', dish: 'carrot cake'}, {id: '3', dish: 'cupcake'}];
const sampe1map = sample1.map(({id}) => id);
const lut = sample2.reduce((acc, {id, dish}) => {
acc[id] = [...(acc[id] || []), dish];
return acc;
}, {});
const res = sampe1map.flatMap(x => lut[x]);
console.log(res);
If your ids in sample2 can be unique, you can use .map() instead of .flatMap() with a new Map() instead for your look up table:
const sample1 = [{id: '1', name: 'jon', quantity: '5'}, {id: '2', name: 'sue', quantity: '4'}, {id: '3', name: 'greg', quantity: '7'}];
const sample2 = [{id: '1', dish: 'cheesecake'}, {id: '2', dish: 'carrot cake'}, {id: '3', dish: 'cupcake'}];
const sampe1map = sample1.map(({id}) => id);
const lut = new Map(sample2.map(({id, dish}) => [id, dish]));
const res = sampe1map.map(x => lut.get(x));
console.log(res);

Categories