Convert array data to object - javascript

so I have this array.
["2020-01-31 18:31:01", "2020-02-03 14:04:30", "2020-02-04 05:58:48", "2020-02-04 14:11:16"]
and I like to convert it to object.
0: {date: "2020-01-31", time: ["8:31:01"]}
1: {date: "2020-02-03", time: ["4:04:30"]}
2: {date: "2020-02-04", time: ["5:58:48", "14:11:16"]}
But I 'm getting this result
0: {date: "2020-01-31", time: "8:31:01"}
1: {date: "2020-02-03", time: "4:04:30"}
2: {date: "2020-02-04", time: "5:58:48"}
This is my code
var times = response.data.time;
var dates = [];
var t = [];
var d = '';
var newData = [];
times.forEach(time => {
var da = time.substring(0, 10);
var ti = time.substring(12, 19);
if(d == da) {
t.push(ti);
} else {
d = da
var dt = {date: da, time: ti};
newData.push(dt);
}
});
I'm having a hard time to figure this out hope you can help me.
Thanks.

Try to use Array.prototype.reduce function:
const array = ["2020-01-31 18:31:01", "2020-02-03 14:04:30", "2020-02-04 05:58:48", "2020-02-04 14:11:16"];
const newData = array.reduce((acc, cur) => {
const [date, time] = cur.split(' ');
const dateObj = acc.find(e => e.date === date);
if (dateObj) {
dateObj.time.push(time);
} else {
acc.push({ date, time: [time] });
}
return acc;
}, []);
console.log(newData);

Almost a copy of Hao Wu's solution, but faster, as it does not rely on linear find. The values are accumulated into an object, allowing for fast lookups, and then an array is extracted using Object.values:
const array = ["2020-01-31 18:31:01", "2020-02-03 14:04:30", "2020-02-04 05:58:48", "2020-02-04 14:11:16"];
const newData = Object.values(array.reduce((acc, cur) => {
const [date, time] = cur.split(' ');
if (!acc[date]) {
acc[date] = { date, time: [] };
}
acc[date].time.push(time);
return acc;
}, {}));
console.log(newData);

Related

Comparing with sliced object string is not working with filter method

i have a array of object like this.
const ExpensesData=[
{
title: 'Toilet Paper',
amount: 94.12,
date: "2022-09-02",
},
{
title: 'New TV',
amount: 799.49,
date: "2021-08-02",
}
]
i am trying to filter objects base upon year.
i tried like this. but, no luck. it looks simple but don't know what is the issue.
const filteredData = ExpensesData.filter((e)=> e.date.slice(0,4) == "2021")
This statement works fine as it return first 4 character of the string.
const aa = ExpensesData[0].date.slice(0,4);
All i am doing is comparing it with.
I came up with this solution.
Maybe it suits you
const ExpensesData =[
{
title: 'Toilet Paper',
amount: 94.12,
date: "2022-08-03",
},
{
title: 'New TV',
amount: 799.49,
date: "2022-08-02",
}
]
const dates = ExpensesData.map(el => el.date);
dates.sort((a, b) => {
let year = [+a.slice(0, 4), +b.slice(0, 4)];
if (comparison(year)) return comparison(year);
let month = [+a.slice(5, 7), +b.slice(5, 7)];
if (comparison(month)) return comparison(month);
let day = [+a.slice(8, a.length), +b.slice(8, b.length)];
if (comparison(day)) return comparison(day);
});
function comparison(arr) {
if (arr[0] > arr[1]) {
return 1;
} else if (arr[0] < arr[1]) {
return -1;
}
}

How to combine all objects into one based on key

Here is the scenario I am looking at:
I want to reduce these objects
const data = [
{
id: 1,
totalAmount: 1500,
date: '2021-01-01',
vendor_id: 2,
totalTransaction: 12,
isRefund: false,
},
{
id: 2,
totalAmount: 200,
date: '2021-01-01',
vendor_id: 2,
totalTransaction: 2,
isRefund: true,
},
{
id: 3,
totalAmount: 200,
date: '2021-01-01',
vendor_id: 2,
totalTransaction: 1,
isRefund: true,
},
];
and I found a solution that adds their values:
const deepMergeSum = (obj1, obj2) => {
return Object.keys(obj1).reduce((acc, key) => {
if (typeof obj2[key] === 'object') {
acc[key] = deepMergeSum(obj1[key], obj2[key]);
} else if (obj2.hasOwnProperty(key) && !isNaN(parseFloat(obj2[key]))) {
acc[key] = obj1[key] + obj2[key]
}
return acc;
}, {});
};
const result = data.reduce((acc, obj) => (acc = deepMergeSum(acc, obj)));
const array = []
const newArray = [...array, result]
which results to:
const newArray = [
{
id: 6,
totalAmount: 1900,
date: '2021-01-012021-01-012021-01-01',
vendor_id: 6,
totalTransaction: 15
}
]
And now my problem is I don't know yet how to work this around to have my expected output which if isRefund is true, it must be subtracted instead of being added, retain the vendor_id and also the concatenated date instead of only one entry date:
const newArray = [
{
id: 1(generate new id if possible),
totalAmount: 1100,
date: '2021-01-01',
vendor_id: 2,
totalTransaction: 15,
isRefund: null(this can be removed if not applicable),
},
];
I will accept and try to understand any better way or workaround for this. Thank you very much.
As you want custom behaviour for several fields, and don't need the recursive aspect of the merge, I would suggest you create a custom merge function, specific to your business logic:
const data = [{id: 1,totalAmount: 1500,date: '2021-01-01',vendor_id: 2,totalTransaction: 12,isRefund: false,},{id: 2,totalAmount: 200,date: '2021-01-01',vendor_id: 2,totalTransaction: 2,isRefund: true,},{id: 3,totalAmount: 200,date: '2021-01-01',vendor_id: 2,totalTransaction: 1,isRefund: true,},];
function customMerge(a, b) {
if (a.vendor_id !== b.vendor_id || a.date !== b.date) {
throw "Both date and vendor_id must be the same";
}
return {
id: Math.max(a.id, b.id),
totalAmount: (a.isRefund ? -a.totalAmount : a.totalAmount)
+ (b.isRefund ? -b.totalAmount : b.totalAmount),
date: a.date,
vendor_id: a.vendor_id,
totalTransaction: a.totalTransaction + b.totalTransaction
};
}
const result = data.reduce(customMerge);
if (data.length > 1) result.id++; // Make id unique
console.log(result);
You could also reintroduce the isRefund property in the result for when the total amount turns out to be negative (only do this when data.length > 1 as otherwise result is just the original single object in data):
result.isRefund = result.totalAmount < 0;
result.totalAmount = Math.abs(result.totalAmount);
Distinct results for different dates and/or vendors
Then use a "dictionary" (plain object or Map) keyed by date/vendor combinations, each having an aggregating object as value.
To demonstrate, I added one more object in the data that has a different date and amount of 300:
const data = [{id: 1,totalAmount: 1500,date: '2021-01-01',vendor_id: 2,totalTransaction: 12,isRefund: false,},{id: 2,totalAmount: 200,date: '2021-01-01',vendor_id: 2,totalTransaction: 2,isRefund: true,},{id: 3,totalAmount: 200,date: '2021-01-01',vendor_id: 2,totalTransaction: 1,isRefund: true,},{id: 4,totalAmount: 300,date: '2021-01-02',vendor_id: 2,totalTransaction: 1,isRefund: false,}];
function customMerge(acc, {id, date, vendor_id, totalAmount, totalTransaction, isRefund}) {
let key = date + "_" + vendor_id;
if (!(id <= acc.id)) acc.id = id;
acc[key] ??= {
date,
vendor_id,
totalAmount: 0,
totalTransaction: 0
};
acc[key].totalAmount += isRefund ? -totalAmount : totalAmount;
acc[key].totalTransaction += totalTransaction;
return acc;
}
let {id, ...grouped} = data.reduce(customMerge, {});
let result = Object.values(grouped).map(item => Object.assign(item, { id: ++id }));
console.log(result);
it could be help, if you are looking for the same output, can add other checklist based on your requirement, for filtered date, logic would be little different but the output will be same.
const getTotalTranscation = () =>
transctionLists.reduce((prev, current) => {
const totalAmount = current.isRefund
? prev.totalAmount - current.totalAmount
: prev.totalAmount + current.totalAmount;
const totalTransaction = current.isRefund
? prev.totalTransaction - current.totalTransaction
: prev.totalTransaction + current.totalTransaction;
return {
...current,
id: current.id + 1,
totalAmount,
totalTransaction,
isRefund: totalAmount < 0,
};
});

Populating an array of object with 0 if object does not exist

I am building a chart for monthly data which would have the x axis as wk1 - wk4 and y axis being the amount of goods etc. I was able to build out a solution but the problem lies when there is no data for a particular week. This is my code below.
const byAmount = obj => {
const res = [];
const keys = Object.keys(obj);
keys.forEach(key => {
res.push({
week: `wk${key}`,
amount: obj[key]
});
});
return res.sort((a, b) => a.amount - b.amount).slice(0, 5);;
};
const getWeeklyFromMonth = (arr, month) => {
const week = arr.map(a => ({ ...a, week: Math.floor((moment(a.dateScanned.$date).date() - 1) / 7) + 1 }));
let dataForMonth = [];
let total;
week.map(data => {
if (moment(data.dateScanned.$date).format("MMM") === month) {
dataForMonth.push(data);
const totalPerWeek = dataForMonth.reduce((acc, cur) => {
acc[cur.week] = acc[cur.week] + cur.amount || cur.amount;
return acc;
}, {});
total = totalPerWeek;
}
});
return byAmount(total);
}
When I run this I get the below:
[
{ week: 'wk1', amount: 14 },
{ week: 'wk2', amount: 27 },
{ week: 'wk4', amount: 43 }
]
This is fine but I want to populate the array with 0 if there is no data say for week 3. I would want it to be this
[
{ week: 'wk1', amount: 14 },
{ week: 'wk2', amount: 27 },
{ week: 'wk3', amount: 0 },
{ week: 'wk4', amount: 43 }
]
I was thinking of having an array of like [1, 2, 3, 4] and if the array includes the week number, pop it out of the array and then the remaining item should be used to populate it but I find myself scratching my head. Does anyone know a decent way to do this?
Thank you in advance.
You can try this:
const byAmount = obj => {
const res = [];
const keys = Object.keys(obj);
const [min, max] = [Math.min(...keys), Math.max(...keys)];
for(let key = min; key <= max; key++) {
res.push({
week: `wk${key}`,
amount: obj[key] || 0
});
}
return res.sort((a, b) => a.amount - b.amount).slice(0, 5);;
};

split an Js object into array

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)

In array of objects turn object values into key/value pair

I am interested to use chartkick and so I need to convert JS object with the following format:
var array = [{date:'01/01/2017',value1:200,value2:300,value3:400}, {date:'02/01/2017',value1:220,value2:330,value3:430},{date:'03/01/2017',value1:250,value2:330,value3:420}]
To the following format:
var arrayOne = [{'01/01/2017': 200}, {'02/01/2017': 220},{'03/01/2017':250}]
Any help would be greatly appreciated.
Use Array.prototype.map():
const src = [{date:'01/01/2017',value1:200,value2:300,value3:400}, {date:'02/01/2017',value1:220,value2:330,value3:430},{date:'03/01/2017',value1:250,value2:330,value3:420}],
result = src.map(({date,value1}) => ({[date]: value1}))
console.log(result)
.as-console-wrapper{min-height:100%;}
You can try this
let sampleArray = [{date:'01/01/2017',value1:200,value2:300,value3:400}, {date:'02/01/2017',value1:220,value2:330,value3:430},{date:'03/01/2017',value1:250,value2:330,value3:420}]
let finalArray = sampleArray.map(data => ({[data.date]:data.value1}))
console.log(finalArray)
Output Will be
[{01/01/2017: 200},{02/01/2017: 220},{03/01/2017: 250}]
var array = [{date:'01/01/2017',value1:200,value2:300,value3:400}, {date:'02/01/2017',value1:220,value2:330,value3:430},{date:'03/01/2017',value1:250,value2:330,value3:420}];
var mappedArray = array.map(item => {
return {
[item.date]: item.value1
}
})
loop the array and map it to the new structure
Try this:
var array = [{date:'01/01/2017',value1:200,value2:300,value3:400}, {date:'02/01/2017',value1:220,value2:330,value3:430},{date:'03/01/2017',value1:250,value2:330,value3:420}]
let final_array = array.map(arr => {
return {[arr.date] : arr.value1};
})
console.log(final_array)
If you want to do it in a loop for each value1, value2, ...
var array = [{
date: '01/01/2017',
value1: 200,
value2: 300,
value3: 400
}, {
date: '02/01/2017',
value1: 220,
value2: 330,
value3: 430
}, {
date: '03/01/2017',
value1: 250,
value2: 330,
value3: 420
}]
const numberOfValues = 3;
for (let i = 1; i <= numberOfValues; i++) {
const mappedArray = array.map(x => {
const result = {};
result[x.date] = x["value" + i.toString()];
return result;
});
console.log(mappedArray);
}

Categories

Resources