I have an object and I need to split the values into an array like below
Ex:
{firstName1: "Tom", lastName1: "Jerry", firstName2: "Stuart", lastName2: "Little"}
I want the output to be like
[{fName: "Tom", lName: "Jerry"},
{fName: "Stuart", lName: "Little"}]
I can do this like
let persons= new Array<Person>(); //Assume Person is a Typescript class
let p1 = new Person();
p1.fName= data.firstName1;
p1.lName= data.lastName1;
persons.push(p1);
let p2= new Person();
p2.fName= data.firstName2;
p2.lName= data.lastName2;
persons.push(p2);
I'm looking for an optimized/recommended way of achieving this.
let requiredObject = [];
let firstNames = [];
let lastNames = [];
let inputObject = {firstName1: "Tom", lastName1: "Jerry", firstName2: "Stuart", lastName2: "Little"}
firstNames = Object.keys(inputObject).filter(each => each.includes('firstName'));
lastNames = Object.keys(inputObject).filter(each => each.includes('lastName'));
for(var i = 0; (firstNames.length > i && lastNames.length > i); i++) {
var data = {};
data.fName = inputObject[firstNames[i]];
data.lName = inputObject[lastNames[i]]
requiredObject.push(data);
}
console.log(requiredObject);
How about this?
let persons= new Array<Person>();
let person = {}; //Not familiar with typescript much. Not sure if it would be {} or new Person();
Object.keys(data).forEach((key,index)=>{
if(index%2 == 0) {
//firstName
person.fName = data[key];
} else {
//lastName
person.lName = data[key];
persons.push(person);
person = {}; // or new Person();
}
});
Update Reduce time complexity by avoiding nested iterations
const combined = {firstName1: "Tom", lastName1: "Jerry", age1: 101, firstName2: "Stuart", lastName2: "Little", age2: 15, petName2: "dog"};
const personMap = Object.entries(combined).reduce((acc, [k, v]) => {
const pNum = k.match(/[0-9]+$/)[0];
const normalKey = k.slice(0, (k.length - pNum.length));
const match = acc.get(pNum),
attr = {[normalKey]: v};
match ?
Object.assign(match, attr) :
acc.set(pNum, {...attr});
return acc;
}, new Map());
const refactored = [...personMap.values()].map(p => ({...p}));
console.log(refactored);
You can use a .reduce() on the keys of the initial object and generate the lastName key from the firstName to avoid ordering problems.
const combined = {firstName1: "Tom", lastName1: "Jerry", firstName2: "Stuart", lastName2: "Little"};
const refactored = Object.keys(combined).reduce((a, k) => {
if (k.startsWith('firstName')){
a.push({fName: combined[k], lName: combined[k.replace('firstName', 'lastName')]});
}
return a;
}, []);
console.log(refactored);
You can generalize this for any number of attributes and pass an array of attributes to map to the new array.
const combined = {firstName1: "Tom", lastName1: "Jerry", age1: 101, firstName2: "Stuart", lastName2: "Little", age2: 15};
const attributes = ['firstName', 'lastName', 'age'];
const refactored = Object.keys(combined).reduce((a, k) => {
if (k.startsWith('firstName')){
const p = attributes.reduce((b, attr) => (b[attr] = combined[k.replace('firstName', attr)], b), {});
a.push({...p});
}
return a;
}, []);
console.log(refactored);
Or for an arbitrary number of attributes .filter the keys of the object by personNumber.
const combined = {firstName1: "Tom", lastName1: "Jerry", age1: 101, firstName2: "Stuart", lastName2: "Little", age2: 15, petName2: "dog"};
const refactored = Object.keys(combined).reduce((a, k) => {
if (k.startsWith('firstName')){
const pNum = k.replace('firstName', '');
const p = Object.keys(combined)
.filter(k => k.endsWith(pNum))
.reduce((b, attr) => (b[attr.replace(pNum, '')] = combined[attr], b), {});
a.push({...p});
}
return a;
}, []);
console.log(refactored);
let obj = {firstName1: "Tom", lastName1: "Jerry", firstName2: "Stuart", lastName2: "Little"}
let keysArr = Object.keys(obj)
let valueArr = Object.values(obj)
let result = []
keysArr.map((x, i) => {
if(x.indexOf('first') != -1){
result.push({ fName: valueArr[i], lName: valueArr[i+1] })
}
})
console.log(result)
Related
I have an array of object and each object is for example :
const myArr=[{name:"john",id:1}{name:"john",id:2}{name:"mary",id:3}]
for the first 2 element for the property "name" I have the name "john" that is duplicate.
How can I modify the rendered names like that:
const myArr=[{name:"john (1 of 2)",id:1}{name:"john (2 of 2)",id:2}{name:"mary",id:3}]
Thanks in advance!
Reduce the input array into a map by name (i.e. group by name property), and map the array of values to the result array. If the group array has more than 1 element in it then sub-map the group to include the numbering. Flatten the overall result.
const myArr = [
{ name: "john", id: 1 },
{ name: "john", id: 2 },
{ name: "mary", id: 3 }
];
const res = Object.values(
myArr.reduce((groups, current) => {
if (!groups[current.name]) {
groups[current.name] = [];
}
groups[current.name].push(current);
return groups;
}, {})
).flatMap((value) => {
if (value.length > 1) {
return value.map((current, i, arr) => ({
...current,
name: `${current.name} (${i + 1} of ${arr.length})`
}));
}
return value;
});
console.log(res);
You can do use reduce(), filter(), and flat() and do this:
const myArr = [
{name:"john", id:1},
{name:"john", id:2},
{name:"mary", id:3}
]
const res = Object.values(myArr.reduce((acc, curr) => {
const total = myArr.filter(({ name }) => name === curr.name).length;
if(!acc[curr.name]) {
acc[curr.name] = [
{...curr}
]
} else {
const currentSize = acc[curr.name].length;
if(currentSize === 1) {
acc[curr.name][0].name = `${acc[curr.name][0].name} (1 of ${total})`
}
acc[curr.name].push({
...curr,
name: `${curr.name} (${currentSize + 1} of ${total})`
})
}
return acc;
}, {})).flat();
console.log(res);
const myArr = [{name:"john",id:1}, {name:"john",id:2}, {name:"mary",id:3}];
const namesArray = myArr.map(elem => elem.name);
const namesTraversed = [];
let currentCountOfName = 1;
let len = 0;
myArr.forEach(elem => {
len = namesArray.filter(name => name === elem.name).length;
if (len > 1) {
if (namesTraversed.includes(elem.name)) {
namesTraversed.push(elem.name);
currentCountOfName = namesTraversed.filter(name => name === elem.name).length;
elem.name = `${elem.name} (${currentCountOfName} of ${len})`;
} else {
namesTraversed.push(elem.name);
currentCountOfName = 1;
elem.name = `${elem.name} (${currentCountOfName} of ${len})`;
}
}
});
console.log(myArr);
Check if this helps you
const myArr = [{
name: "john",
id: 1
}, {
name: "john",
id: 2
}, {
name: "mary",
id: 3
}]
// to keep a track of current copy index
let nameHash = {}
const newMyArr = myArr.map(ele => {
const noOccurence = myArr.filter(obj => obj.name ===ele.name).length;
if(noOccurence > 1){
// if there are multiple occurences get the current index. If undefined take 1 as first copy index.
let currentIndex = nameHash[ele.name] || 1;
const newObj = {
name: `${ele.name} (${currentIndex} of ${noOccurence})`,
id: ele.id
}
nameHash[ele.name] = currentIndex+ 1;
return newObj;
}
return ele;
})
console.log(newMyArr);
I am able to get all unique properties from an array like this,
var array = [{
"firstName": "John",
"lastName": "Doe"
}, {
"firstName": "Anna",
"car": true
}, {
"firstName": "Peter",
"lastName": "Jones"
}];
var result = [];
array.reduce( function(pre, item) {
Object.keys(item).forEach(function(i){
if (result.indexOf(i) === -1){
result.push(i);
}
});
});
console.log(result);
However now I need this output,
[{
"firstName":"John, Anna, Peter",
"car": "true",
"lastName": "Doe, Jones"
}]
but I am not sure how to ?
Various ways. Here's one:
//get unique properties - hence Set, not array, so dups are omitted
let props = new Set(array.map(obj => Object.keys(obj)).flat());
//get non-falsy value for each prop type, as an array
let vals = [...props].map(prop => array.map(obj => obj[prop]).filter(a => a).join(', '));
//merge the two via Object.fromEntries()
let final = Object.fromEntries.call(null, [...props].map((prop, i) => [prop, vals[i]]));
You could achieve that by having a lookup object that store all values for a property. Then manipulate that object by joining all occurences
Below snippet could help you
var array = [
{
firstName: "John",
lastName: "Doe",
},
{
firstName: "Anna",
car: true,
},
{
firstName: "Peter",
lastName: "Jones",
},
]
var lookup = {}
var result = {}
array.forEach((obj) => {
Object.entries(obj).forEach(([key, val]) => {
if (!lookup[key]) {
lookup[key] = [val]
} else {
lookup[key].push(val)
}
})
})
Object.entries(lookup).forEach(([key, val]) => {
result[key] = val.join(', ')
})
console.log(result)
hi I am trying to create a object map from array of objects using reduce method but did n't find a way to add 2 properties as key . Let say I have array of objects like -
const students = [
{
name: "sam",
age: 26,
},
{
name: 'john",
age: 30,
}
]
i am trying to create a map like
{
sam_26:{
name: "sam",
age: 26,
}
}
my code for reduce function :
students.reduce((obj, student) => {
`${obj[student.name]}_${obj[student.age]}` = student;
return obj;
}, {});
this didn't work . any pointers will be helpful ..thanks!
Create the key with the values taken from the student object. Assign the current student to the obj (the accumulator) using the key:
const students = [{
name: "sam",
age: 26,
},
{
name: "john",
age: 30,
}
];
const result = students.reduce((obj, student) => {
const key = `${student.name}_${student.age}`;
obj[key] = student;
return obj;
}, {});
console.log(result);
A generic approach that uses a callback to create the key:
const keyBy = (arr, cb) =>
arr.reduce((r, o) => {
const key = cb(o);
r[key] = o;
return r;
}, {});
const students = [{"name":"sam","age":26},{"name":"john","age":30}];
const result = keyBy(students, (o) => `${o.name}_${o.age}`);
console.log(result);
You can't assign to the left side with a template literal like that. Try defining the property first, and then assigning it to the object:
const students = [ { name: "sam", age: 26, }, { name: 'john', age: 30, } ];
const finalObj = students.reduce((obj, student) => {
const prop = `${student.name}_${student.age}`;
obj[prop] = student;
return obj;
}, {});
console.log(finalObj);
Hopefully this snippet will be useful
const students = [{
name: "sam",
age: 26,
},
{
name: "john",
age: 30,
}
]
//Using reduce function to add value to the accumalator
var x = students.reduce(function(acc, curr, index) {
// Here acc is the object which is passed as argument,
//In this object checking if it has a key like sam_26 & so on
if (!acc.hasOwnProperty([curr['name'] + '_' + curr['age']])) {
//if not then add the key and add relevant vakues to it
acc[curr.name + '_' + curr.age] = {
name: curr.name,
age: curr.age
}
}
return acc;
}, {});
console.log(x)
I tried this script and it worked. Simply create variable name based on student name and age then assign back to the object
students.reduce((obj, student) => {
var name = student.name + '-' + student.age;
obj[name] = student;
return obj;
}, {});
What is the best way to transform an array like this:
const arr = [
{ name: 'Bob' },
{ name: 'Ben' }
{ name: 'Cole' }
{ name: 'Mary' }
{ name: 'Travis' }
]
to an object like:
const obj = {
'B': ['Bob', 'Ben'],
'C': ['Cole'],
'M': ['Mary'],
'T': ['Travis']
}
Using only vanilla JS
You can use array#reduce. Iterate through each object of your array and then extract out the first letter and add names corresponding to it.
const arr = [{name: 'Bob'}, {name: 'Ben'}, {name: 'Cole'}, {name: 'Mary'}, {name: 'Travis'}],
result = arr.reduce((r,{name}) => {
r[name[0]] = r[name[0]] || [];
r[name[0]].push(name);
return r;
},{});
console.log(result);
Vanilla JS you say? Here you go
let nil = x => x === undefined;
let empty = ([h]) => nil(h);
let first = ([h]) => h;
let last = ([h, ...t]) => empty(t) ? h : last(t);
let map = ([h, ...t], f) => nil(h) ? [] : [f(h), ...map(t, f)];
let reduce = ([h, ...t], f, i) => nil(h) ? i : reduce(t, f, f(i, h));
let tab = (a, f) => map(a, x => [x, f(x)]);
let push = (a, x) => nil(a) ? [x] : [...a, x];
let groupBy = (a, f) => _groupBy(tab(a, f));
let _groupBy = ka => reduce(ka, (g, [x, k]) => ({...g, [k]: push(g[k], x)}), {});
///
const arr = [{ name: 'Bob' },{ name: 'Ben' },{ name: 'Cole' },{ name: 'Mary' },{ name: 'Travis' }]
z = groupBy(map(arr, x => x.name), first)
console.log(z)
No built-ins!
I created an array where the key is the first letter of the name using the reduce function and restructuring the 'name' from the objects. If the key exists in the array the name is pushed (using spread operator). Else, it creates the key with only one element.
const arr = [
{ name: 'Bob' },
{ name: 'Ben' },
{ name: 'Cole' },
{ name: 'Mary' },
{ name: 'Travis' }
];
const obj = arr.reduce((res, {name})=>{
res[name[0]] = res[name[0]] ? [...res[name[0]],name] : [name];
return res;
}, {});
console.log(obj);
I think this thread is missing a non functional answer, so here it is:
const obj = {};
for(const {name} of arr)
(obj[name[0]] || (obj[name[0]] = [])).push({name});
let obj = {};
arr.forEach( e => {
const name = e.name;
if (!obj[name.charAt(0)]) obj[name.charAt(0)] = [];
obj[name.charAt(0)].push(name);
})
I'm generating a new object and adding to it news keys based in the first char of the name values (only if the key hasn't been already added).
Then, I add each value to the key that corresponds.
I have a model returned by a library in the following format:
var givenData = [{"fName": "john"}, {"fName": "mike"}, {"country": "USA"}]
How do I group the "fName" together and add '[]' to get:
{ 'fName[]': ['john','mike'],'country[]': ['USA'] };
**Note country and fName are not related at all.
Suggestion (using ES6 syntax)
const transformData = (data) => {
const newData = {}
data.forEach( (item) => {
for (let key in item) {
const newKey = key + "[]"
if (!newData.hasOwnProperty(newKey)) newData[newKey] = []
newData[newKey].push(item[key])
}
})
return newData
}
/* added some extra keys just to test */
let givenData = [
{"fName": "john", "country": "England"},
{"fName": "mike"},
{"country": "USA"},
{"language": "English"}
]
console.log(transformData(givenData))
/*
{
"fName[]": ["john","mike"],
"country[]": ["England","USA"],
"language[]":["English"]
}
*/
You can iterate over the array and push the date to the desired field.
var givenData = [{"fName": "john"}, {"fName": "mike"}, {"country": "USA"}]
var result = {
'fName[]': [],
'country[]': []
};
givenData.forEach(function (data) {
if (data.fName) {
result['fName[]'].push(data.fName);
}
if (data.country) {
result['country[]'].push(data.country);
}
});
console.log(result);
You could take the key and build an object with the key and arrays as properties.
var givenData = [{"fName": "john"}, {"fName": "mike"}, {"country": "USA"}],
grouped = givenData.reduce(function (r, o) {
var key = Object.keys(o)[0] + '[]';
r[key] = r[key] || [];
r[key].push(o[Object.keys(o)[0]]);
return r;
}, Object.create(null));
console.log(grouped);
In ES6:
const newData = givenData.reduce(function (r, o) {
const key = `${Object.keys(o)[0]}[]`;
return { ...r, [key]: [ ...r[key], o[key] ] }
}, {});
This doesn't modify your original data and is very clean.