I have an array as below
array1 = [{"month":"January","location":"CENTRAL","percentage":94},
{"month":"February","location":"CENTRAL","percentage":97},
{"month":"March","location":"CENTRAL","percentage":93},
{"month":"January","location":"NORTH","percentage":95},
{"month":"February","location":"NORTH","percentage":91},
{"month":"March","location":"NORTH","percentage":98}];
I want to format my array to look as below
array2= [{
location: "CENTRAL",
January: 94,
February: 97,
March: 93},
{
location: "NORTH",
January: 95,
February: 91,
March: 98}];
I tried to use group by function as below
function groupBy(list, keyGetter) {
const map = new Map();
list.forEach((item) => {
const key = keyGetter(item);
if (!map.has(key)) {
map.set(key, [item]);
} else {
map.get(key).push(item);
}
});
return map;
}
const grouped = groupBy(array1, arr => arr.location);
console.log(grouped);
but I am not getting the same format. Any suggestions please what I am missing in my idea ? Thank you very much.
You can indeed use a map keyed by location, but then with plain objects as values. The problem in your code is that you push items into an array, and don't turn the month values into properties of a singular object (per location).
So given a map with plain objects, iterate the input and inject the month properties into those plain objects, with corresponding percentage values, and then take the values of that map:
const array1 = [{"month":"January","location":"CENTRAL","percentage":94},{"month":"February","location":"CENTRAL","percentage":97},{"month":"March","location":"CENTRAL","percentage":93},{"month":"January","location":"NORTH","percentage":95},{"month":"February","location":"NORTH","percentage":91},{"month":"March","location":"NORTH","percentage":98}];
const map = new Map(array1.map(({location}) => [location, { location }]));
array1.forEach(o => map.get(o.location)[o.month] = o.percentage);
const result = [...map.values()];
console.log(result);
You could use a dynamic approach by handing over the group key, the keys for key and value for collecting.
Then add all key/value pairs to the object and later return the values of the map.
function groupBy(list, group, key, value) {
return Array.from(list
.reduce((map, object) => map.set(object[group], Object.assign(
map.get(object[group]) || { [group]: object[group] },
{ [object[key]]: object[value] }
)), new Map)
.values()
);
}
var array = [{ month: "January", location: "CENTRAL", percentage: 94 }, { month: "February", location: "CENTRAL", percentage: 97 }, { month: "March", location: "CENTRAL", percentage: 93 }, { month: "January", location: "NORTH", percentage: 95 }, { month: "February", location: "NORTH", percentage: 91 }, { month: "March", location: "NORTH", percentage: 98 }],
result = groupBy(array, 'location', 'month', 'percentage');
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
An alternative is using the function reduce to group by location and the function Object.values to extract the grouped objects.
let array1 = [{"month":"January","location":"CENTRAL","percentage":94},{"month":"February","location":"CENTRAL","percentage":97},{"month":"March","location":"CENTRAL","percentage":93},{"month":"January","location":"NORTH","percentage":95},{"month":"February","location":"NORTH","percentage":91},{"month":"March","location":"NORTH","percentage":98}],
groupBy = (array, keygetter) => {
return Object.values(array1.reduce((a, {month, percentage, ...rest}) => {
let key = keygetter(rest);
(a[key] || (a[key] = {location: key}))[month] = percentage;
return a;
}, Object.create(null)));
},
result = groupBy(array1, (o) => o.location);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
One approach is to use .filter() and .map()
var array1 = [{"month":"January","location":"CENTRAL","percentage":94},
{"month":"February","location":"CENTRAL","percentage":97},
{"month":"March","location":"CENTRAL","percentage":93},
{"month":"January","location":"NORTH","percentage":95},
{"month":"February","location":"NORTH","percentage":91},
{"month":"March","location":"NORTH","percentage":98}];
const groupBy = (arr, prop, propName1, propName2, propName3) =>
Object.assign({[prop]:propName1}
, ...arr.filter(({[prop]:p}) => p === propName1)
.map(({[propName2]:m, [propName3]:p}) => ({[m]: p}))
);
const locations = ["CENTRAL", "NORTH"];
let res = locations.map(prop => groupBy(array1, "location", prop, "month", "percentage"));
console.log(res);
The problem is, you can't just rearrange the properties in an object. According to the ECMAScript specifications:
An ECMAScript object is an unordered collection of properties
You can rearrange the properties using your function, but the order will not be guaranteed in the final object.
Here is another example using reduce, pass the callback function and specify the key in it
array1 = [{
"month": "January",
"location": "CENTRAL",
"percentage": 94
},
{
"month": "February",
"location": "CENTRAL",
"percentage": 97
},
{
"month": "March",
"location": "CENTRAL",
"percentage": 93
},
{
"month": "January",
"location": "NORTH",
"percentage": 95
},
{
"month": "February",
"location": "NORTH",
"percentage": 91
},
{
"month": "March",
"location": "NORTH",
"percentage": 98
}
];
function groupBy(list, callback) {
return list.reduce((acc, x) => {
const key = callback(x);
if (!acc[key]) {
return {
...acc,
[key]: [x]
}
}
return {
...acc,
[key]: [...acc[key], x]
}
}, {})
}
// callback function
function locationName(obj) {
return obj.location
}
console.log('group by location name:', groupBy(array1, locationName));
Related
I need to create an array of array.
It is worth noting that the database is very large and that if any attribute does not have a corresponding value, it sends an empty string. I've tried with map and reduce but I wasn't successful:
Any help will be appreciated.
Below I show an example of the expected output:
outputExpected = [
["id", 1, 2],
["name", "name1", "name2"],
["price", 6.95, 998.95],
["promoPrice", 5.91, 333.91],
["category", "test1 | test2", "test3 | test4"],
]
Any way to solve this problem performatically?
this is my code:
let arrayObj = [{
"id": 1,
"name": "name1",
"price": 6.95,
"promoPrice": 5.91,
"category": ["test1, test2"]
},
{
"id": 2,
"name": "name2",
"price": 998.95,
"promoPrice": 333.91,
"category": ["test3, test4"]
}
]
const headers = ["id", "name", "price", "promoPrice", "category"]
const result1 = headers.concat(arrayObj.map((obj) => {
return headers.reduce((arr, key) => {
arr.push(obj[key]) return arr;
}, [])
}))
console.log(result1)
Reduce the array to a Map. On each iteration convert the object to an array of [key, value] pairs using Object.entries(). Use Array.forEach() to iterate the entries and add them to the map. Convert the Map's values iterator to an array using Array.from():
const arr = [{"id":1,"name":"name1","price":6.95,"promoPrice":5.91,"category":["test1", "test2"]},{"id":2,"name":"name2","price":998.95,"promoPrice":333.91,"category":["test3", "test4"]}]
const result = Array.from(arr.reduce((acc, o) => {
Object.entries(o)
.forEach(([k, v]) => {
if(!acc.has(k)) acc.set(k, [k])
acc.get(k).push(Array.isArray(v) ? v.join(' | ') : v)
})
return acc
}, new Map()).values())
console.log(result)
You could simply map the value and check if an item is an array, then take the joined values or the value itself.
const
data = [{ id: 1, name: "name1", price: 6.95, promoPrice: 5.91, category: ["test1, test2"] }, { id: 2, name: "name2", price: 998.95, promoPrice: 333.91, category: ["test3, test4"] }],
headers = ["id", "name", "price", "promoPrice", "category"],
result = data
.reduce(
(r, o) => headers.map((k, i) => [
...r[i],
Array.isArray(o[k]) ? o[k].join(' | ') : o[k]
]),
headers.map(k => [k]),
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
construct one init array with all possible keys with the wanted order, then uses Array.reduce and Array.forEach to Array.push value for per key based on its index.
const arrayObj = [
{
"id":1,
"name":"name1",
"price":6.95,
"promoPrice":5.91,
"category":["test1", "test2"]
},
{
"id":2,
"name":"name2",
"price":998.95,
"promoPrice":333.91,
"category":["test3", "test4"]
}
]
function ConvertToArray2D (items) {
let init = [['id'], ['name'], ['price'], ['promoPrice'], ['category']]
if (!items) return init
return arrayObj.reduce((pre, cur) => {
init.forEach((key, index) => {
pre[index].push(Array.isArray(cur[key[0]]) ? cur[key[0]].join('|') : cur[key[0]])
})
return pre
}, init.slice())
}
console.log(ConvertToArray2D(arrayObj))
This can be handled with a standard 'zip' after mapping your objects to arrays of values in line with the headers array. (This also allows for the result to be pivoted back).
//** #see https://stackoverflow.com/a/10284006/13762301
const zip = (...rs) => [...rs[0]].map((_, c) => rs.map((r) => r[c]));
const headers = ['id', 'name', 'price', 'promoPrice', 'category'];
const arrayObj = [{ id: 1, name: 'name1', price: 6.95, promoPrice: 5.91, category: ['test1', 'test2'] },{ id: 2, name: 'name2', price: 998.95, promoPrice: 333.91, category: ['test3', 'test4'] },];
const result = zip(
headers,
...arrayObj.map((o) => headers.map(h => Array.isArray(o[h]) ? o[h].join(' | ') : o[h]))
);
console.log(result);
// which also allows it to be reversed
console.log(zip(...result));
.as-console-wrapper { max-height: 100% !important; top: 0; }
see: Javascript equivalent of Python's zip function for further zip discussion.
This is the input data in array named candidatesArray:
[
{"name":"george","languages":["php","javascript","java"],"age":19,"graduate_date":1044064800000,"phone":"32-991-511"},
{"name":"anna","languages":["java","javascript"],"age":23,"graduate_date":1391220000000,"phone":"32-991-512"},
{"name":"hailee","languages":["regex","javascript","perl","go","java"],"age":31,"graduate_date":1296525600000,"phone":"32-991-513"}
]
I need to transform in this collection as a result of the function:
{candidates: [
{name: "George", age: 19, phone: "32-991-511"},
{name: "Hailee", age: 31, phone: "32-991-513"},
{name: "Anna", age: 23, phone: "32-991-512"}
],
languages: [
{lang:"javascript",count:1},
{lang:"java", count:2},
{lang:"php", count:2},
{lang:"regex", count:1}
]}
The function repCandidates:
const reportCandidates = (candidatesArray) => {
return repObject}
I need to write it in javascript ES6
I shouldn't use loops(for, while, repeat) but foreach is allowed and it could be better if I use "reduce" function
The candidates should be return by their name, age and phone organized by their graduate_date.
The languages should be returned with their counter in alphabetic order .
Visit https://codepen.io/rillervincci/pen/NEyMoV?editors=0010 to see my code, please.
One option would be to first reduce into the candidates subobject, while pushing the langauges of each to an array.
After iterating, sort the candidates and remove the graduate_date property from each candidate, then use reduce again to transform the languages array into one indexed by language, incrementing the count property each time:
const input = [{
"name": "george",
"languages": ["php", "javascript", "java"],
"age": 19,
"graduate_date": 1044064800000,
"phone": "32-991-511"
}, {
"name": "anna",
"languages": ["java", "javascript"],
"age": 23,
"graduate_date": 1391220000000,
"phone": "32-991-512"
}, {
"name": "hailee",
"languages": ["regex", "javascript", "perl", "go", "java"],
"age": 31,
"graduate_date": 1296525600000,
"phone": "32-991-513"
}];
const output = input.reduce((a, { languages, ...rest }) => {
a.candidates.push(rest);
a.languages.push(...languages);
return a;
}, { candidates: [], languages: [] });
output.candidates.sort((a, b) => a.graduate_date - b.graduate_date);
output.candidates.forEach(candidate => delete candidate.graduate_date);
output.languages = Object.values(
output.languages.reduce((a, lang) => {
if (!a[lang]) a[lang] = { lang, count: 0 };
a[lang].count++;
return a;
}, {})
);
output.languages.sort((a, b) => a.lang.localeCompare(b.lang));
console.log(output);
It's common practice to do everything in a reduce(), but sometimes it's easier to read if you break it up a bit. This creates a counter object as a helper to to track the language counts. map()s over the array to pull out the languages and personal info and then puts it all together:
let arr = [ {"name":"george","languages":["php","javascript","java"],"age":19,"graduate_date":1044064800000,"phone":"32-991-511"},{"name":"anna","languages":["java","javascript"],"age":23,"graduate_date":1391220000000,"phone":"32-991-512"},{"name":"hailee","languages":["regex","javascript","perl","go","java"],"age":31,"graduate_date":1296525600000,"phone":"32-991-513"}]
let lang_counter = {
// helper keeps counts of unique items
counts:{},
add(arr){
arr.forEach(item => this.counts[item] = this.counts[item] ? this.counts[item] + 1 : 1)
},
toarray(){
return Object.entries(this.counts).map(([key, val]) => ({[key]: val}))
}
}
// iterate over object to create candidates
let candidates = arr.map(row => {
let {languages, ...person} = row
lang_counter.add(languages) // side effect
return person
})
// put them together
console.log({candidates, languages:lang_counter.toarray()})
You can use Array.reduce and Object.values like below
let arr = [{"name":"george","languages":["php","javascript","java"],"age":19,"graduate_date":1044064800000,"phone":"32-991-511"},{"name":"anna","languages":["java","javascript"],"age":23,"graduate_date":1391220000000,"phone":"32-991-512"},{"name":"hailee","languages":["regex","javascript","perl","go","java"],"age":31,"graduate_date":1296525600000,"phone":"32-991-513"}]
let res = arr.reduce((o, {name, age, phone, graduate_date, languages}) => {
o.candidates.push({name, age, phone, graduate_date})
languages.forEach(l => {
o.languages[l] = o.languages[l] || { lang:l, count: 0 }
o.languages[l].count++
})
return o
}
, { candidates: [], languages: {}})
res.candidates = res.candidates.sort((a,b) => a.graduate_date - b.graduate_date)
.map(({ graduate_date, ...rest }) => rest)
res.languages = Object.values(res.languages).sort((a,b) => a.lang.localeCompare(b.lang))
console.log(res)
I need to add events to the existing date so i think i need to change my json structure something like below. I tried to achieve it via reduce as shown below
I want to convert this array of object
"sunday": [
"...",
7,
14,
21,
28
]
To
{
"sunday":[
{
"7":[
"event1",
"event2",
"event3"
]
},
{
"14":[
"event3",
"event4",
"event5"
]
},
{
"21":[]
},
{
"28":[]
}
]
}
Here is the data that has event's detail,
{
"data": [
{ "date": "7",
"events": ["event1", "event2", "event3"]
},
{ "date": "14",
"events": ["event3", "event4", "event5"]
},
]
}
What i tried and failed,
attachEventsToTheDate(week_days) {
var answer = [week_days].reduce(function(result, item, index) {
var key = Object.keys(item)[0];
var value = item[key][index];
var obj = {};
obj[key] = [obj[key]];
console.log('obj is', JSON.stringify(obj));
// JSON.stringify(obj);
// result.push(obj);
return result;
}, {}); //an empty array
}
map it:
var sunday = [ 7, 14, 21, 28 ]
var data = [ { "date": "7", "events": ["event1", "event2", "event3"] }, { "date": "14", "events": ["event3", "event4", "event5"] }]
console.log(sunday.map(x => {
var actual = data.find(y => y.date == x);
if(actual) {
return {[x]: actual.events}
} else {
return {[x]: []}
}
}))
.as-console-wrapper { max-height: 100% !important; top: 0; }
The object structure you desire to have will not be very efficient to work with, but here it is:
function groupEvents(week_days, events) {
const numToDay = []; // Extra index into the week_days nested objects
// Create a new week_days object
week_days = Object.assign(...Object.keys(week_days).map( day =>
({ [day]: week_days[day].filter(Number).map(num =>
({ [num]: numToDay[num] = [] })
)})
));
// Inject the events data into it
for (const data of events.data) {
// Skip if this day does not exist in the original week_days structure
if (!numToDay[data.date]) continue;
numToDay[data.date].push(...data.events);
}
return week_days;
}
const week_days = {sunday: ["...", 7, 14, 21, 28]},
events = {data: [
{ date: "7", events: ["event1", "event2", "event3"] },
{ date: "14", events: ["event4", "event5"] }
]};
const res = groupEvents(week_days, events);
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A more generic solution
The week_days object seems to be a translation of a certain month. If one has just the month, the week_days structure can be derived from it. So the key information is really your events object. If you can turn that object (as specified in your original question, i.e. with year and month info included) into something grouped by year-month combinations, and then produce for each combination a week_days kind of structure, you'll have all you need.
At the same time it seems more logical to use the day numbers as keys in the week_days nested objects.
Here is a demo on how you can do that:
const events = {
"data": [
{ "year": "2017", "month": "january", "date": "16",
"events": ["event1", "event2", "event3"]
},
{ "year": "2017", "month": "january", "date": "8",
"events": ["event4", "event5"]
},
]
};
function groupEvents(events) {
const dayNames = ['sunday', 'monday', 'tuesday', 'wednesday',
'thursday', 'friday', 'saturday'];
return events.data.reduce( (acc, o) => {
const key = o.month.toLowerCase() + " " + o.year;
if (!acc[key]) {
acc[key] = Object.assign(...dayNames.map(day => ({ [day]: {} })));
const dt = new Date("1 " + key);
const month = dt.getMonth();
while (dt.getMonth() === month) {
acc[key][dayNames[dt.getDay()]][dt.getDate()] = [];
dt.setDate(dt.getDate()+1);
}
}
const dt = new Date(o.date + " " + key);
acc[key][dayNames[dt.getDay()]][dt.getDate()].push(...o.events);
return acc;
}, {});
}
const res = groupEvents(events);
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If now you want to have only the data of the month January 2017 (for example), you can just take it out of the result from the above script as follows:
const january2017_week_days = res["january 2017"];
I want to do a grouping for JavaScript object as age.My JSON is
var jsonObj=[{"name":"john","age":23},{"name":"mark","age":25},{"name":"jeni","age":21}]`
I want to be the group result like here.
[23:{"name":"john","age":23},25:{"name":"mark","age":25},21:{"name":"jeni","age":21}]
Please help me to get a result, I try with map and filter but did not get the result.
Use Array#reduce to group the objects. Because there can multiple people with the same age, collect them into an array under the age property:
var jsonObj=[{"name":"john","age":23},{"name":"mark","age":25},{"name":"poll","age":23},{"name":"jeni","age":21}];
var result = jsonObj.reduce(function(r, o) {
r[o.age] || (r[o.age] = []); // if the age key doesn't exist, set it to be an array
r[o.age].push(o); // push the object into the array
return r;
}, {});
console.log(result);
Or the fancy ES6 one liner version:
var jsonObj=[{"name":"john","age":23},{"name":"mark","age":25},{"name":"poll","age":23},{"name":"jeni","age":21}];
var result = jsonObj.reduce((r, o) => ((r[o.age] || (r[o.age] = [])).push(o), r), {});
console.log(result);
You could take the hash table as result. Then loop and create new arrays for new keys. Later push the object.
var data = [{ name: "john", age: 23 }, { name: "mark", age: 25 }, { name: "poll", age: 23 }, { name: "jeni", age: 21 }],
result = {};
data.forEach(o => (result[o.age] = result[o.age] || []).push(o));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use underscore js to solve this problem.Add underscore js first<script src='https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js' ></script> then you can simply get the result.
var result=_.indexBy(jsonObj, 'age');
You can try it :
var jsonObj = [{ "name": "john", "age": 23 }, { "name": "mark", "age": 25 }, { "name": "poll", "age": 23 }, { "name": "jeni", "age": 21 }];
let result = {};
jsonObj.forEach(item => {
if(result.hasOwnProperty(item.age)) {
let arr = [];
if(Array.isArray(result[item.age])) {
result[item.age].push(item);
} else {
result[item.age] = [result[item.age], item];
}
} else {
result[item.age] = item;
}
});
console.log(result)
I am looking for merge arrays of object into single array of object and append key of object to each key from inner object's key
I have object like
var myObj = {
"Details": [{
"car": "Audi",
"price": 40000,
"color": "blue"
},
{
"car": "BMW",
"price": 35000,
"color": "black"
}
],
"Accounts": [{
"Total": 2000
},
{
"Total": 3000
}
]
}
and Keys and objects length is not known, I want to merge it like
[
{
"Detailscar": "Audi",
"Detailsprice": 40000,
"Detailscolor": "blue",
"AccountsTotal": 2000
},
{
"Detailscar": "BMW",
"Detailsprice": 35000,
"Detailscolor": "black",
"AccountsTotal": 3000
}
]
I have tried with Ramda mergeAll but it is not working in my case as it only merge objects
here is what I tried
var mergedArray = []
R.mapObjIndexed((instance, instanceName) => {
mergedArray.push(R.map((innerObj) => {
var customObject = {};
R.forEach((key) => {
customObject[`${instanceName}${key}`] = innerObj[key]
}, Object.keys(innerObj))
return customObject;
}, instance))
}, myObj)
I am trying add to each modified object to the mergerArray array but it adding for each iteration and finally, it is creating 2 arrays
mergedArray is still creating two different arrays with the key of the object to be appended to the properties of the object but I want it to be merged in the single array of object.
I am missing something. What should I do to resolve this issue?
Suggest some help.
Use Array.prototype.map and index as second parameter passsed to its callback to get element from Account object
const data = {
"Details": [
{
"car": "Audi",
"price": 40000,
"color": "blue"
},
{
"car": "BMW",
"price": 35000,
"color": "black"
},
{
"car": "Porsche",
"price": 60000,
"color": "green"
}
],
"Accounts": [
{
"Total": 2000
},
{
"Total": 3000
},
{
"Total": 3000
}
]
};
const mergeCarData = ({ Details, Accounts} = {}) => {
return Details.length === Accounts.length ? Details.map(({ car, price, color}, idx) => ({
Detailscar: car,
Detailsprice: price,
Detailscolor: color,
AccountsTotal: Accounts[idx].Total
})) : [];
};
console.log(mergeCarData(data));
In plain Javascript, you could iterate the keys of the given object and iterate the arrays and build a new object out of the inner properties with a new key.
var object = { Details: [{ car: "Audi", price: 40000, color: "blue" }, { car: "BMW", price: 35000, color: "black" }], Accounts: [{ Total: 2000 }, { Total: 3000 }] },
result = Object.keys(object).reduce(function (returnValue, parentKey) {
object[parentKey].forEach(function (currentObj, i) {
returnValue[i] = returnValue[i] || {};
Object.keys(currentObj).forEach(function (childKey) {
returnValue[i][parentKey + childKey] = currentObj[childKey];
});
});
return returnValue;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Well, it's not pretty, but you could do something like this:
const convert = pipe(
mapObjIndexed((val, name) => pipe(
map(toPairs),
map(map(([prop, propVal]) => objOf(name + prop, propVal))),
map(mergeAll),
)(val)),
values,
apply(zipWith(merge))
)
You can see this in action on the Ramda REPL.
Here you go, another option. I pulled a renameBy function from ramda recipe list. You can combine all the stuff on to one line if you want.
// https://github.com/ramda/ramda/wiki/Cookbook#rename-keys-of-an-object
const renameBy = R.curry((fn, obj) => R.pipe(R.toPairs, R.map(R.adjust(fn, 0)), R.fromPairs)(obj));
let convertNames = R.mapObjIndexed((value, key)=> {
return R.map(renameBy(R.concat(key)), value)
})
let mergeUp = R.pipe(R.values, R.reduce(R.mergeDeepLeft,[]), R.values)
let convert = R.pipe(convertNames, mergeUp)
convert(myObj)