Related
From This object how can I get the result mentioned bellow?
const obj = {
gopal: [{ height: "164" }, { weight: "75" }, { balance: "500" }],
liton: [{ salary: "20000" }, { balance: "1000" }],
};
I want this result:
const newObj = {height: "164", weight: "75", balance: "500", salary: "20000", balance: "1000"}
My try:
const objc2 = {
gopal: [{
height: "164"
}, {
weight: "75"
}, {
balance: "500"
}],
liton: [{
salary: "20000"
}, {
balance: "1000"
}],
};
function outp(obj) {
const objKeys = Object.keys(obj);
let final = {};
objKeys.forEach((item) => {
let res = {};
obj[item].forEach((item2) => {
res = { ...res,
...item2
};
});
final = { ...final,
[item]: res
};
});
console.log(Object.values(final).reduce(function(result, current) {
return Object.assign(result, current);
}, {}));
}
outp(objc2);
I know it is not the best way to do this, but it is an option
const obj = {
gopal: [{ height: "164" }, { weight: "75" }, { balance: "500" }],
liton: [{ salary: "20000" }, { balance: "1000" }],
};
let json = JSON.stringify(obj);
let keys = Object.keys(obj);
keys.forEach(k => json = json.replaceAll(`"${k}":`, ""));
json = json.replaceAll(/[\[\]\{\}]/g, "");
json = "{" + json + "}";
console.log(json);
const obj2 = JSON.parse(json);
console.log(obj2);
If you are happy using an array when two items have the same key, you could do something like:
const input = {
gopal: [{ height: "164" }, { weight: "75" }, { balance: "500" }],
liton: [{ salary: "20000" }, { balance: "1000" }],
};
const output = Object.values(input).flat().reduce((outObj, item) => {
const [key, value] = Object.entries(item)[0];
outObj.hasOwnProperty(key) // if the key is already present
? Array.isArray(outObj[key]) // if the attribute is already an array
? outObj[key].push(value) // push the value in the array
: outObj[key] = [outObj[key], value] // create an array with the current value + the new one
: outObj[key] = value; // otherwise add a new key/value pair to the object
return outObj;
}, {});
// test
console.log(output);
Object.values(input) would return a 2D array
Then you use flat() to make a single dimension array from the 2D one
Finally you use reduce() to loop through the array and produce the output object
Then you can access the values of the "duplicated" keys with output.key[index] (e.g. output.balance[0])
I use the following reduce:
const data = this.forms.reduce((accumulator, current) => {
return (accumulator[current.name] = current.value);
}
}, {});
Where this.forms is:
[
{value: {document: "fsfsf", seria: "fsfsfsf"}, "name": "Form1"},
{value: {seria: "AA", age: "45"}, "name": "Form2"},
{value: {marry: "yes", hobby: "AAA"}, "name": "Form3"}
]
I need to build this result:
{
"Form1": {document: "fsfsf", seria: "fsfsfsf"},
"Form2": {seria: "AA", age: "45"},
"Form3": {marry: "yes", hobby: "AAA"}
}
But I get wrong result:
{
{document: "fsfsf", seria: "fsfsfsf"},
"Form2": {}
}
I can not get why?
The callback function to the accumulator for reduce must return the accumulator to be applied to subsequent elements in the array. Returning the result of an assignment expression returns the result of the expression (current.value in this case), not the accumulator.
const forms = [
{value: {document: "fsfsf", seria: "fsfsfsf"}, "name": "Form1"},
{value: {seria: "AA", age: "45"}, "name": "Form2"},
{value: {marry: "yes", hobby: "AAA"}, "name": "Form3"}
];
const data = forms.reduce((accumulator, current) => {
accumulator[current.name] = current.value;
return accumulator;
}, {});
console.log(data);
Suppose I got this array:
const users =[
{
id:1,
name:'bob',
},
{
id:2,
name:'sally',
},
{
id:3,
name:'bob',
age:30,
}
];
And I want to use any key(in this case 'name' ) to return an object :
{
bob:[
{
id:1,
name:'bob',
},
{
id:3,
name:'bob',
age:30,
}
],
sally:[
{
id:2,
name:'sally',
}
],
}
I tried this:
const go = (A,key) =>{
return A.reduce((o, key) => ({ ...o, [key]:o }), {})
}
export default go;
But this returns:
{ '[object Object]': { '[object Object]': { '[object Object]': {} } } }
If the key is not present omit from the result. It should not mutate the original array though. How can I perform this kind of conversion?
With the approach you have, a new array is not instantiated in case the key is not yet present in the object.
This will work:
const result = users.reduce((a, v) => {
a[v.name] = a[v.name] || [];
a[v.name].push(v);
return a;
}, {});
Complete snippet wrapping this logic in a function:
const users = [{
id: 1,
name: 'bob',
}, {
id: 2,
name: 'sally',
}, {
id: 3,
name: 'bob',
age: 30,
}];
const go = (input, key) => input.reduce((a, v) => {
a[v[key]] = a[v[key]] || [];
a[v[key]].push(v);
return a;
}, {});
console.log(go(users, 'name'));
If you really want to cram it into a one-liner, this will also work, by either spreading the already existing array, or an empty one:
const result = users.reduce((a, v) => ({...a, [v.name]: [...a[v.name] || [], v]}), {});
Complete snippet wrapping this logic in a function:
const users = [{
id: 1,
name: 'bob',
}, {
id: 2,
name: 'sally',
}, {
id: 3,
name: 'bob',
age: 30,
}];
const go = (input, key) => input.reduce((a, v) => ({...a, [v[key]]: [...a[v[key]] || [], v]}), {});
console.log(go(users, 'name'));
You were close but the key attribute in this case was each value (eg: { id: 1, name: 'bob' }) so the string representation is [object Object] which is why all the keys are that. Based off what you said, you want to use key.name as the property and set it's value as [key]. (I renamed key to arr in my example since it's the array value).
So this would be something like { ...o, [arr.name]: [arr] }
Because there can be an existing value, it adds a bit of complexity which is what [...(obj[arr.name] || []), arr] is doing. It's looking up the existing value (or defaulting to an empty array) and spreading those values and adding the new value.
const users = [{
id: 1,
name: 'bob',
},
{
id: 2,
name: 'sally',
},
{
id: 3,
name: 'bob',
age: 30,
}
];
const transform = (input, keyName) => {
return input.reduce((obj, arr) => ({ ...obj,
[arr[keyName]]: [...(obj[arr[keyName]] || []), arr]
}), {})
}
console.log(transform(users, 'name'))
console.log(transform(users, 'id'))
I have an array of arrays below. With ES6, how can I get a count of each value Good, Excellent & Wow into a new array e.g [{name: Good, count: 4} {name: Excellent, count: 5}, {name:Wow, count:2}] in dynamic style. I am attempting to use Object.assign but I am failing to "unique" out the count of the key plus instead, I need to use an array as I am trying to render this out on the front end. Do I need to use reduce? how?
let k = 0
const stats = {}
const remarks = [
[{name: "Good"}],
[{name: "Good"}, {name: "Excellent"}],
[{name: "Good"}, {name: "Excellent"}, {name: "Wow"}],
[{name: "Good"}, {name: "Excellent"}, {name: "Wow"}],
[{name: "Excellent"}],
[{name: "Excellent"}]
]
remarks.forEach((arr) => {
arr.map((e) => {
Object.assign(stats, { [e.name]: k = k + 1 })
})
})
console.log(stats);
Output:
stats: {Good: 8, Excellent: 11, Wow: 9}
Which is Incorrect plus I need to use an array.
Expected output:
[{name: Good, count: 4} {name: Excellent, count: 5}, {name:Wow, count:2}]
Flatten the array of arrays and reduce it starting with an object like : { Good: 0, Excellent: 0, Wow: 0}
then .map the Object.entries of the result to transform it to an array :
const remarks = [
[{ name: "Good" }],
[{ name: "Good" }, { name: "Excellent" }],
[{ name: "Good" }, { name: "Excellent" }, { name: "Wow" }],
[{ name: "Good" }, { name: "Excellent" }, { name: "Wow" }],
[{ name: "Excellent" }],
[{ name: "Excellent" }]
];
const result = Object.entries(
remarks.flat().reduce(
(all, { name }) => {
all[name] += 1;
return all;
},
{ Good: 0, Excellent: 0, Wow: 0 }
)
).map(([name, count]) => ({ name, count }));
console.log(result);
You can try below logic:
var data = [[{name: "Good"}],[{name: "Good"}, {name:"Excellent"}],[{name: "Good"}, {name:"Excellent"}, {name:"Wow"}],[{name: "Good"}, {name:"Excellent"}, {name:"Wow"}],[{name:"Excellent"}],[{name:"Excellent"}]]
var nData = [];
(data || []).forEach( e => {
(e || []).forEach(ei => {
var i = (index = nData.findIndex(d => d.name === ei.name)) >=0 ? index : nData.length;
nData[i] = {
name: ei.name,
count : (nData[i] && nData[i].count ? nData[i].count : 0)+1
}
});
});
console.log(nData);
Hope this helps!
You can use reduce, then convert the result into an array of objects:
const counts = remarks.reduce((result, list) => {
list.forEach(remark => {
result[remark.name] = (result[remark.name] || 0) + 1;
});
}, {});
const finalResult = [];
for (let name in counts) {
finalResult.push({name, count: counts[name]});
}
You could achieve this pretty easily by:
1) Flattening the nested array into 1 single level array.
2) Iterating over the flat array and create a "count map" by using Array.prototype.reduce
For example:
const remarks = [
[{
name: 'Good'
}],
[{
name: 'Good'
}, {
name: 'Excellent'
}],
[{
name: 'Good'
}, {
name: 'Excellent'
}, {
name: 'Wow'
}],
[{
name: 'Good'
}, {
name: 'Excellent'
}, {
name: 'Wow'
}],
[{
name: 'Excellent'
}],
[{
name: 'Excellent'
}]
]
const flatten = arr => arr.reduce((accum, el) => accum.concat(el), [])
const map = flatten(remarks).reduce((accum, el) => {
if (accum[el.name]) {
accum[el.name] += 1;
} else {
accum[el.name] = 1;
}
return accum;
}, {});
console.log(map)
First find the counts using reduce than pass that to another function to get the desired view structure:
const Good = 1,
Excellent = 2,
Wow = 3;
const remarks = [
[{name: Good}],
[{name: Good}, {name:Excellent}],
[{name: Good}, {name:Excellent}, {name:Wow}],
[{name: Good}, {name:Excellent}, {name:Wow}],
[{name:Excellent}],
[{name:Excellent}]
];
/*
[{name: Good, count: 4} {name: Excellent, count: 5}, {name:Wow, count:2}]
*/
function counts(remarks) {
return remarks.flat().reduce((acc, v) => {
const name = v.name;
let count = acc[name] || 0;
return {
...acc,
[name]: count + 1
}
}, {});
}
function view(counts) {
return Object.keys(counts).map(key => {
let count = counts[key];
return { name: key, count };
})
}
console.log(view(counts(remarks)));
Any time you are making a smaller set of data, or transforming data, in JavaScript reduce should be the first method you attempt to use. In this case, you may want to pair it with an indexer (hence preloading with an array of index and an array of result).
This works in one pass without needing to know the name values up front.
const remarks = [
[{name: "Good"}],
[{name: "Good"}, {name: "Excellent"}],
[{name: "Good"}, {name: "Excellent"}, {name: "Wow"}],
[{name: "Good"}, {name: "Excellent"}, {name: "Wow"}],
[{name: "Excellent"}],
[{name: "Excellent"}]
];
const stats = remarks.reduce((p,c) => (
c.forEach( ({name}) => {
if(!p[0].hasOwnProperty(name)){
p[1].push({name:name,count:0});
p[0][name] = p[1].length - 1;
}
p[1][p[0][name]].count++;
}),p),[{},[]])[1];
console.log(stats);
A slightly more concise and definitely less readable approach (but it's worth to mention) could be:
const remarks = [
[{ name: "Good" }],
[{ name: "Good" }, { name: "Excellent" }],
[{ name: "Good" }, { name: "Excellent" }, { name: "Wow" }],
[{ name: "Good" }, { name: "Excellent" }, { name: "Wow" }],
[{ name: "Excellent" }],
[{ name: "Excellent" }]
];
const stats = Object.entries(
remarks
.flat()
.reduce((acc, {name}) => (acc[name] = -~acc[name], acc), {})))
).map(([name, count]) => ({ name, count }));
console.log(stats);
It uses the comma operator in the reducer to returns the accumulator; and the bitwise operator NOT to create a counter without the needs to initialize the object upfront with all the names.
const flattenedRemarks = _.flatten(remarks);
const groupedRemarks = _.groupBy(flattenedRemarks, (remark) => remark.name);
const remarkCounts = _.mapValues(groupedRemarks, (group) => group.length);
const data = {
"mchale": {
"classes":["ESJ030", "SCI339"], // get the length
"faculty":["Hardy", "Vikrum"] // get the length
},
"lawerence":{
"classes":["ENG001"], // get the length
"faculty":["Speedman", "Lee", "Lazenhower"] // get the length
}
};
const count = Object.keys(data).map(campusName => {
const campus = data[campusName];
return Object.keys(campus).map(key => campus[key].length).reduce((p, c) => p + c, 0);
}).reduce((p, c) => p + c, 0);
console.log(count);
If I have the following Array:
var myArray = [
{sa67g:{id:'sa67g', name: 'Leo', value: 50}},
{sa86w:{id:'sa86w', name: 'Amy', value: 40}},
{sa33p:{id:'sa33p', name: 'Alex', value: 30}},
{sa74x:{id:'sa74x', name: 'John', value: 20}},
{sa67g:{id:'sa67g', name: 'Leo', value: 10}},
{sa33p:{id:'sa33p', name: 'Alex', value: 15}},
]
What is the best one to get a single array with no-repeated items, with the sum of their value(s) and ordered by descending value for all the repeated items from another array using lodash?
Te expected result should be something like this:
result = [{sa67g:{id:'sa67g', name: 'Leo', value: 60}},
{sa33p:{id:'sa33p', name: 'Alex', value: 45}},
{sa86w:{id:'sa86w', name: 'Amy', value: 40}},
{sa74x:{id:'sa74x', name: 'John', value: 20}}
]
I'm sure there are a bunch of ways to do it. Off the top of my head, I would use reduce to convert your array of objects into one object with summed values. Using chain to combine with some other lodash methods for ordering and transforming, it would look like this:
const result = _.chain(myArray)
.map(person => _.values(person))
.flatten()
.reduce((summed, person) => {
if (!summed[person.id]) {
summed[person.id] = person
} else {
summed[person.id].value += person.value
}
return summed
}, {})
.values()
.orderBy(['value'], ['desc'])
.map(person => ({ [person.id]: person }))
.value()
You can use lodash's _.mergeWith() to combine all objects into a single object. Since _.mergeWith is recursive, the inner properties will be merged as well, and we can use this to sum the value property. Afterwards, we convert the object back to an array using _.map():
const myArray = [{"sa67g":{"id":"sa67g","name":"Leo","value":50}},{"sa86w":{"id":"sa86w","name":"Amy","value":40}},{"sa33p":{"id":"sa33p","name":"Alex","value":30}},{"sa74x":{"id":"sa74x","name":"John","value":20}},{"sa67g":{"id":"sa67g","name":"Leo","value":10}},{"sa33p":{"id":"sa33p","name":"Alex","value":15}}];
const result = _.map(
// merge all objects into a single object, and sum the value property
_.mergeWith({}, ...myArray, (objValue = 0, srcValue = 0, key) => key === 'value' ? objValue + srcValue : undefined),
// split back into an array of objects
(v, k) => ({ [k]: v })
)
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Easiest way to filter out single values would be to use a Set. Use it to get a set of unique keys.
Create the desired object by iterating the keys and calculating the sums.
Using native methods and assuming es8 support:
var myArray = [
{sa67g:{id:'sa67g', name: 'Leo', value: 50}},
{sa86w:{id:'sa86w', name: 'Amy', value: 40}},
{sa33p:{id:'sa33p', name: 'Alex', value: 30}},
{sa74x:{id:'sa74x', name: 'John', value: 20}},
{sa67g:{id:'sa67g', name: 'Leo', value: 10}},
{sa33p:{id:'sa33p', name: 'Alex', value: 15}},
]
var tmp = myArray.map(o => Object.values(o)[0])
.reduce((a, c) => {
const obj = a[c.name]
if (obj) {
obj.id = c.id > obj.id ? c.id : obj.id;// set highest id
obj.value += c.value; //increment vlues
} else {
a[c.name] = Object.assign({},c);
}
return a;
}, {});
var res = Object.values(tmp)
.sort((a, b) => b.value - a.value)
.map(o => ({[o.id]:o }))
console.log(res)
.as-console-wrapper { max-height: 100%!important;}
You could take a step by step approach and take the resutl for the next function with a pipe.
var array = [{ sa67g: { id: 'sa67g', name: 'Leo', value: 50 } }, { sa86w: { id: 'sa86w', name: 'Amy', value: 40 } }, { sa33p: { id: 'sa33p', name: 'Alex', value: 30 } }, { sa74x: { id: 'sa74x', name: 'John', value: 20 } }, { sa67g: { id: 'sa67g', name: 'Leo', value: 10 } }, { sa33p: { id: 'sa33p', name: 'Alex', value: 15 } }],
pipe = (...fn) => arg => fn.reduce((x, f) => f(x), arg),
objects = array => array.map(o => Object.assign({}, Object.values(o)[0])),
sum = array => array.reduce((m, o) => {
if (m.has(o.id)) {
m.get(o.id).value += o.value;
} else {
m.set(o.id, o);
}
return m;
}, new Map),
values = map => Array.from(map.values()),
sort = array => array.sort((a, b) => b.value - a.value),
map = array => array.map(o => ({ [o.id]: o })),
path = pipe(objects, sum, values, sort, map),
result = path(array);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Object properties should never be a value (ie, a key or id) – to create a key-value association, use a Map instead.
const concat = ({ id: id1, name: name1, value: v1 }, { id: id2, name: name2, value: v2 }) =>
({ id: id1, name: name1, value: v1 + v2 })
const main = xs =>
xs.map (x => Object.entries (x) [ 0 ])
.reduce ((acc, [ key, value ]) =>
acc.has (key)
? acc.set (key, concat (value, acc.get (key)))
: acc.set (key, value), new Map)
const myArray =
[ {sa67g:{id:'sa67g', name: 'Leo', value: 50}}
, {sa86w:{id:'sa86w', name: 'Amy', value: 40}}
, {sa33p:{id:'sa33p', name: 'Alex', value: 30}}
, {sa74x:{id:'sa74x', name: 'John', value: 20}}
, {sa67g:{id:'sa67g', name: 'Leo', value: 10}}
, {sa33p:{id:'sa33p', name: 'Alex', value: 15}}
]
console.log (main (myArray))
// => Map {
// 'sa67g' => { id: 'sa67g', name: 'Leo', value: 60 },
// 'sa86w' => { id: 'sa86w', name: 'Amy', value: 40 },
// 'sa33p' => { id: 'sa33p', name: 'Alex', value: 45 },
// 'sa74x' => { id: 'sa74x', name: 'John', value: 20 } }
If you want to convert the Map back to an object, you can use this
const mapToObject = m =>
Array.from(m.entries ()).reduce ((acc, [ k, v ]) =>
Object.assign (acc, { [ k ]: v }), {})