How to create sorted object by date - javascript

I'am trying to solve an issue that requires the sort of an array of objects containing information. For example, transform this:
[
{
name: 'test1',
date: '2019-02-18T08:33:01.000Z',
},
{
name: 'test2',
date: '2019-02-19T08:33:01.000Z',
},
{
name: 'test3',
date: '2019-02-19T08:33:01.000Z',
},
]
To this:
{
'2019-02-18':{
name: 'test1'
},
'2019-02-19':[
{
name: 'test2',
},
{
name: 'test3',
}
]
}
How can i achieve this?

Assuming you want an array of objects for every grouped date. One alternative is to use Array.reduce() in conjunction with String.match()
const input = [
{name: 'test1', date: '2019-02-18T08:33:01.000Z'},
{name: 'test2', date: '2019-02-19T08:33:01.000Z'},
{name: 'test3', date: '2019-02-19T08:33:01.000Z'}
];
let res = input.reduce((acc, {name, date}) =>
{
let [m, d] = date.match(/(.+)T/);
acc[d] = [...(acc[d] || []), {name}];
return acc;
}, {});
console.log(res);

This is more a group by problem than a sort. Object keys are unsorted, also.
function groupByDate(data) {
let result = {};
data.forEach((el) => {
let datePart = el.date.split("T")[0];
let value = result[datePart]
if(value) {
result[datePart].push({name: el.name})
} else {
result[datePart] = [{name: el.name}]
}
});
return result;
}
var dates = [
{
name: 'test1',
date: '2019-02-18T08:33:01.000Z',
},
{
name: 'test2',
date: '2019-02-19T08:33:01.000Z',
},
{
name: 'test3',
date: '2019-02-19T08:33:01.000Z',
},
];
console.log(groupByDate(dates));

Related

JavaScript: how can I merge these two arrays of incomplete objects and make an array of complete objects

I have two arrays of objects
const a = [
{ name: 'apple', type: 'fruit' },
{ name: 'berry', type: 'fruit' },
{ name: 'grape', type: 'fruit' },
{ name: 'broccoli', type: 'vegetable' },
{ name: 'cabbage', type: 'vegetable' },
]
const b = [
{ name: 'apple', amount: 4 },
{ name: 'berry', amount: 5 },
{ name: 'grape', amount: 3 },
{ name: 'broccoli', amount: 7 },
{ name: 'avocado', amount: 8 },
]
I need to write a function to output an array with objects with the same name being merged into one.
const c = [
{ name: 'apple', type: 'fruit', amount: 4 },
{ name: 'berry', type: 'fruit', amount: 5 },
{ name: 'grape', type: 'fruit', amount: 3 },
{ name: 'broccoli', type: 'vegetable', amount: 7 },
{ name: 'cabbage', type: 'vegetable', amount: 0 },
{ name: 'avocado', type: undefined, amount: 8 },
]
As you can see here, objects that share the same name are merged into one object with a few exceptions:
if type field is missing, we would need to add it and make it undefined
if amount field is missing, we need to add it and make it 0
Here is my attempt:
function fillMissingFields(object) {
console.log('object', object)
let newObject = { ...object }
if (object.type === undefined) {
newObject = { ...object, type: undefined }
}
if (object.amount === undefined) {
newObject = { ...newObject, amount: 0 }
}
return newObject
}
function join(a, b) {
const results = []
for (const aItem of a) {
const bItems = b.filter((item) => item.name === aItem.name)
let newObject
if (bItems.length) {
for (const bItem of bItems) {
newObject = { ...newObject, ...bItem }
}
newObject = fillMissingFields({ ...newObject, ...aItem })
} else {
newObject = fillMissingFields(aItem)
}
results.push(newObject)
}
return results
}
Besides the fact that it has a really bad time complexity O(n^2). It actually has a bug where if an object only appears in b array, that object will be omitted entirely from the new array.
Can anyone try to help me come up with a more robust and efficient algorithm to tackle this problem?
Make a collection whose keys are the names, whose values are the combined objects, which starts out with an undefined type and an amount of 0. Iterate through both arrays, assigning the property values as needed, then at the end, take the collection's values:
const a = [
{ name: 'apple', type: 'fruit' },
{ name: 'berry', type: 'fruit' },
{ name: 'grape', type: 'fruit' },
{ name: 'broccoli', type: 'vegetable' },
{ name: 'cabbage', type: 'vegetable' },
];
const b = [
{ name: 'apple', amount: 4 },
{ name: 'berry', amount: 5 },
{ name: 'grape', amount: 3 },
{ name: 'broccoli', amount: 7 },
{ name: 'avocado', amount: 8 },
];
const objsByName = new Map();
const getObj = (name) => {
if (!objsByName.has(name)) {
objsByName.set(name, { name, type: undefined, amount: 0 });
}
return objsByName.get(name);
};
for (const { name, type } of a) {
getObj(name).type = type;
}
for (const { name, amount } of b) {
getObj(name).amount = amount;
}
console.log([...objsByName.values()]);
This following approach will work for dynamic objects which have different keys other than name, type or amount. Although there is one problem with that approach we can not define default values for each variable(like for amount default will be 0 or for other key default will be undefined). In my approach if any key is missing in any of the object the default value will be undefined.
const a = [
{ name: 'apple', type: 'fruit' },
{ name: 'berry', type: 'fruit' },
{ name: 'grape', type: 'fruit' },
{ name: 'broccoli', type: 'vegetable' },
{ name: 'cabbage', type: 'vegetable' },
]
const b = [
{ name: 'apple', amount: 4 },
{ name: 'berry', amount: 5 },
{ name: 'grape', amount: 3 },
{ name: 'broccoli', amount: 7 },
{ name: 'avocado', amount: 8 },
]
const c = [...a, ...b];
const s = new Set();
let d = c.reduce((acc, curr) => {
const index = acc.findIndex(item => item.name === curr.name);
if(index > -1) {
acc[index] = {...acc[index], ...curr};
} else {
acc.push(curr);
}
Object.keys(curr).forEach(key => s.add(key));
return acc;
}, []);
let res = d.map(item => {
let keyInObj = Object.keys(item);
Array.from(s).forEach(actualKey => {
if(!keyInObj.includes(actualKey)) {
item[actualKey] = undefined;
}
});
return item;
})
console.log(res);
Please find below code snippet creating a unique array of objects out of two arrays. If value is not present in amount or type, we are writing 0 or undefined respectively.
const a = [
{ name: "apple", type: "fruit" },
{ name: "berry", type: "fruit" },
{ name: "grape", type: "fruit" },
{ name: "broccoli", type: "vegetable" },
{ name: "cabbage", type: "vegetable" },
];
const b = [
{ name: "apple", amount: 4 },
{ name: "berry", amount: 5 },
{ name: "grape", amount: 3 },
{ name: "broccoli", amount: 7 },
{ name: "avocado", amount: 8 },
];
let d = [...a, ...b];
const c = [];
d.map((o) => {
const uniqIndex = c.findIndex((item) => item.name === o.name);
uniqIndex === -1
? c.push({
amount: 0,
type: undefined,
...o,
})
: (c[uniqIndex] = {
...c[uniqIndex],
...o,
});
});
console.log(c);

How to replace a key inside a nested object

I have this list
{
'0': { id: 'id3', name: 'Capitan America', job: 'superHero' },
'1': { id: 'id4', name: 'Spider-man', job: 'Pro footballer' }
}
And i want to change my keys to the id value like this:
{
id3 : { id: 'id3', name: 'Capitan America', job: 'superHero' },
id4 : { id: 'id4', name: 'Spider-man', job: 'Pro footballer' }
}
this is what i have tried in my code where in my fetch items i transform an array of objects into an object of objects and when i transform into one object of object my keys stayed like the index of the array:
fetchItems() {
const objectified = Object.assign({},this.list)
return objectified;
}
When you use Object.assign to assign an array to an object, indices of the array will become properties of the assigned object. The best way to construct your desired object is to build it manually.
Modify your fetchItems as below:
function fetchItems() {
const result = {};
for (const item of list)
result[item.id] = item;
return result;
}
Here is one way to do what you are asking by stepping over each property, building a new object.
var objA = {
'0': { id: 'id3', name: 'Capitan America', job: 'superHero' },
'1': { id: 'id4', name: 'Spider-man', job: 'Pro footballer' }
};
function transform(obj) {
var newObj = {};
for(p in obj) { newObj[obj[p].id] = obj[p]; }
return newObj;
}
var objB = transform(objA);
console.log(objB);
You could use Object.entries and iterate through the key-value pairs with .reduce
const data = {
0: { id: "id3", name: "Capitan America", job: "superHero" },
1: { id: "id4", name: "Spider-man", job: "Pro footballer" },
}
const res = Object.entries(data).reduce(
(acc, el) => ({ ...acc, [el[1].id]: el[1] }),
{}
)
console.log(res)
Map the array to pairs of [id, object], and then convert to an object using Object.fromEntries():
const arr = [
{ id: 'id3', name: 'Capitan America', job: 'superHero' },
{ id: 'id4', name: 'Spider-man', job: 'Pro footballer' }
]
const result = Object.fromEntries(arr.map(o => [o.id, o]))
console.log(result)

Why async.map returns multiple copies of array?

const async = require('async');
const arr = [
{ name: 'john', id: '1' },
{ name: 'Andrie', id: '2' }]
let collectArr = [];
let data = async.mapLimit(arr, 5, async function (input) {
collectArr.push({ name: input.name, id: input.id });
return collectArr;
})
data.then((result) =>{
console.log('success',result);
}).catch(e => console.log('err'));
So here i am providing array to async.mapLimit without callback and expecting promise here.
Expected Output :- [ { name: 'john', id: '1' }, { name: 'Andrie', id: '2' } ] ,
Got Result :-
[ [ { name: 'john', id: '1' }, { name: 'Andrie', id: '2' } ],
[ { name: 'john', id: '1' }, { name: 'Andrie', id: '2' } ] ]
So my question is why it is creating multiple copies of array, how to deal with this?
You are needlessly returning a sub array, and the same array reference each iteration, when all you want is to return the new object.
let data = async.mapLimit(arr, 5, async function (input) {
return { name: input.name, id: input.id };
});
Not sure why you need this to be async

JavaScript operation array object merge

I have two arrays, and now I want to merge the two arrays.
The first array:
var data = [
{ name: 'aa', value: 1 },
{ name: 'bb', value: 2 },
{ name: 'cc', value: 3 }
];
Two arrays:
var data2 = [
{ name: 'aa' },
{ name: 'bb' },
{ name: 'cc' },
{ name: 'dd' },
{ name: 'ee' }
];
I want to merge them into this:
var data3 = [
{name: 'aa', value: 1},
{name: 'bb', value: 2},
{name: 'cc', value: 3},
{name: 'dd', value: 0},
{name: 'ee', value: 0}
];
console.log(data3)
At present, my experience is not enough. Please help me solve this problem.
Thanks in advance.
You can try following based on following assumptions
data2 is a collection of names and expecting its length to be always more than length of data
Order of objects can be different
var data = [
{ name: 'aa', value: 1 },
{ name: 'bb', value: 2 },
{ name: 'cc', value: 3 }
];
var data2 = [
{ name: 'aa' },
{ name: 'bb' },
{ name: 'cc' },
{ name: 'dd' },
{ name: 'ee' }
];
// Iterate over the names array
var data3 = data2.map(({name}) => {
// get the matched object in data corresponding to the name
var match = data.find((obj) => obj.name === name);
// if found, return value else default value to 0
return match ? match : {name, value : 0};
});
console.log(data3);
If the input arrays are indeed in order like that, then a simple .map would suffice:
var data = [
{ name: 'aa', value: 1 },
{ name: 'bb', value: 2 },
{ name: 'cc', value: 3 }
];
var data2 = [
{ name: 'aa' },
{ name: 'bb' },
{ name: 'cc' },
{ name: 'dd' },
{ name: 'ee' }
];
const output = data2.map(({ name }, i) => ({ name, value: data[i] ? data[i].value : 0 }));
console.log(output);
Create an object lookup for each name using array#reduce. Extract all the values using the Object.values() from the object lookup.
const data1 = [ { name: 'aa', value: 1 }, { name: 'bb', value: 2 }, { name: 'cc', value: 3 } ],
data2 = [ { name: 'aa' }, { name: 'bb' }, { name: 'cc' }, { name: 'dd' }, { name: 'ee' } ],
result = Object.values([data1, data2].reduce((r,a) => {
a.forEach(({name, value = 0}) => {
r[name] = name in r ? r[name] : {name, value};
});
return r;
},{}));
console.log(result);
You can array#concat both the arrays and using array#reduce create an object lookup and then get all the values using the Object.values().
const data1 = [ { name: 'aa', value: 1 }, { name: 'bb', value: 2 }, { name: 'cc', value: 3 } ],
data2 = [ { name: 'aa' }, { name: 'bb' }, { name: 'cc' }, { name: 'dd' }, { name: 'ee' } ],
result = Object.values(data1.concat(data2).reduce((r,{name, value=0}) => {
r[name] = name in r ? r[name] : {name, value};
return r;
},{}));
console.log(result);
var data = [
{ name: 'aa', value: 1 },
{ name: 'bb', value: 2 },
{ name: 'cc', value: 3 }
];
var data2 = [
{ name: 'aa' },
{ name: 'bb' },
{ name: 'cc' },
{ name: 'dd' },
{ name: 'ee' }
];
let output = new Array(data2.length).fill(data2.length).map(v => new Object());
// Logic
data.forEach((val2)=> {
data2.forEach((val, i)=> {
if (val.name == val2.name){
output[i]["name"] = val.name
output[i]["value"] = val2.value
} else{
output[i]["name"] = val.name
}
})
})
output.map((val,i) => {
if (!val.hasOwnProperty("value")){
console.log(val)
val["value"] = 0
}
})
console.log("------Your Expected Format", output)

Transform the data

I have the following data structure:
const data = [
{
name: 'ABC',
salesData: [
{
timestamp: '2017-09-01',
value: 10
},
{
timestamp: '2017-09-02',
value: 2
}
]
},
{
name: 'DEF',
salesData: [
{
timestamp: '2017-09-01',
value: 8
},
{
timestamp: '2017-09-02',
value: 3
}
]
}
];
I would like to transform this to:
[
{
name: 'ABC',
'2017-09-01': 10,
'2017-09-02': 2
},
{
name: 'CDE',
'2017-09-01': 8,
'2017-09-02': 3
}
]
I'm trying to use Underscore's Chain and Map which I'm getting confused. So far I have the following, not sure how do I write the convertedSalesData to transform as per the need:
_.map(data, function(item) {
let name = item.name;
let salesData = item.salesData;
let convertedSalesData = ?
})
With ES6 you can use spread syntax ... to get this result.
const data = [{"name":"ABC","salesData":[{"timestamp":"2017-09-01","value":10},{"timestamp":"2017-09-02","value":2}]},{"name":"DEF","salesData":[{"timestamp":"2017-09-01","value":8},{"timestamp":"2017-09-02","value":3}]}]
var result = data.map(function({name, salesData}) {
return {name, ...Object.assign({}, ...salesData.map(({timestamp, value}) => ({[timestamp]: value})))}
})
console.log(result)
const data = [{
name: 'ABC',
salesData: [{
timestamp: '2017-09-01',
value: 10
},
{
timestamp: '2017-09-02',
value: 2
}
]
},
{
name: 'DEF',
salesData: [{
timestamp: '2017-09-01',
value: 8
},
{
timestamp: '2017-09-02',
value: 3
}
]
}
];
var res = data.map(function(a) {
var obj = {
name: a.name
};
a.salesData.forEach(function(x) {
obj[x.timestamp] = x.value;
})
return obj;
})
console.log(res);
Similar to #Nenad Vracar. I perfer to use 'reduce':
data.map(({ name, salesData }) => ({
name,
...salesData.reduce(
(record, { timestamp, value }) => {
record[timestamp] = value
return record
},
Object.create(null)
)
}))

Categories

Resources