Related
So I have an array of objects that contain information for different activities in different projects.
It looks something like this
const input = [
{
Project: 1,
ID: "1-1",
},
{
Project: 1,
ID: "1-2",
},
{
Project: 2,
ID: "2-1",
},
];
From this, I would like to go to this
output = [{
Project: 1,
ID1: 1 - 1,
ID2: 1 - 2,
},
{
Project: 1,
ID1: 2 - 1,
},
];
Here's what I have so far:
let currentProject = ''
let output = []
for (const e of input) {
let counter
let outputObj = {}
if (currentProject !== e.Project) {
output.push(outputObj)
counter = 1
outputObj = {}
outputObj.projectNum = e.Project
currentProject = e.Project
}
if (currentProject == e.Project) {
outputObj['ID' + counter] = e.ID
counter++
}
}
here's what I'm getting back:
output = [{
Project: 1,
ID1: 1 - 1
},
{
Project: 1,
ID1: 2 - 1
}
]
I'm not sure what the issue is, tried several times to fix it.
Could someone please help me get over the edge?
Any help will be greatly appreciated.
You can achieve this using reduce, Object.keys
const input = [{
Project: 1,
ID: "1-1",
},
{
Project: 1,
ID: "1-2",
},
{
Project: 2,
ID: "2-1",
},
];
const result = input.reduce((acc, curr) => {
const { Project, ID } = curr;
const obj = acc.find((el) => el.Project === Project);
if (obj) {
const length = Object.keys(obj).length;
obj[`ID${length}`] = ID;
} else {
acc.push({ Project, [`ID${1}`]: ID });
}
return acc;
}, []);
console.log(result);
You can try this.
const input=[{Project:1,ID:"1-1",},{Project:1,ID:"1-2",},{Project:2,ID:"2-1"}];
let temp = {};
input.map(v=>(temp[v.Project] ??= []).push(v.ID));
let output = Object.keys(temp).map(k=>{
let json = {Project:k};
temp[k].map((v,k)=>json['ID'+(Number(k+1))]=v);
return json;
});
console.log(output);
you will get the result.
[
{ Project: '1', ID1: '1-1', ID2: '1-2' },
{ Project: '2', ID1: '2-1' }
]
The way you intended to implement this assumes every project from same id comes sequentially grouped.
While #decpk answer deals with with a linear search, for performance reasons I would rather first use a dictionary and then convert to an array, AND also keep track of id quantities using a field n.
const input = [
{
Project: 1,
ID: "1-1",
},
{
Project: 1,
ID: "1-2",
},
{
Project: 2,
ID: "2-1",
},
];
const projects = {}
for (const e of input) {
let pid = e.Project
let project = projects[pid]
//First time seeing this project
if (!project) {
projects[pid] = { Project: pid, n: 1, ID1: e.ID }
}
//insert more ID
else {
project.n += 1
project[`ID${project.n}`] = e.ID
}
}
//And now converting the object to array, removing the 'n' field
const output = Object.keys(projects).map(pid => {
const obj = projects[pid]
delete obj.n
obj.Project = pid
return obj
})
You can try this way - O(n) time complexity
Using reduce to aggregate data.
Using logical nullish assignment only assigns if acc[Project] is nullish (null or undefined).
Define each additional key-value pair like:
const number = Object.keys(acc[Project]).length; // Define key format by number of existing property.
const key = `ID${number}`;
instead of using count variable.
const input=[{Project:1,ID:"1-1",},{Project:1,ID:"1-2",},{Project:2,ID:"2-1",}];
const output = input.reduce((acc, {Project, ID}) =>
{
acc[Project] ??= {Project}; // Get exist object or create new one
const number = Object.keys(acc[Project]).length; // Define key format by number of existing property.
const key = `ID${number}`;
acc[Project][key] = ID;
return acc;
}, {});
console.log(Object.values(output));
Output:
[
{
"Project": 1,
"ID1": "1-1",
"ID2": "1-2"
},
{
"Project": 2,
"ID1": "2-1"
}
]
var values =selectValues;
var names = selectNames;
var priorities = prioritizedHours;
var prefers = preferHrsArray;
var years = workedYearsArray;
var items = values.map((value, index) => {
return {
value: value,
name: names[index],
priority: priorities[index],
prefer: prefers[index],
year: years[index]
}
});
var arrayObject = JSON.stringify(items);
Logger.log('Object array: '+arrayObject);
In the above program, I am creating an object from the arrays such as names, priorities, and so on. Resulting Object is following after I have made a sorting of them:
[
{"value":1,"name":"Fiona","prefer":30,"year":6},
{"value":1,"name":"Martin","prefer":40,"year":7},
{"value":2,"name":"Adam","prefer":0,"year":20},
{"value":2,"name":"Steve","prefer":100,"year":5}
]
Now as sorting is done, I want the arrays back as they are in the Object.
I am trying to get arrays like:
value = [1,1,2,2],
name = ['Fiona', 'Martin','Adam', 'Steve'],
prefer = [30,40,0,100],
year = [6,7,20,5]
Thank you for helping me out.
You can use forEach for this case
const array = [
{"value":1,"name":"Fiona","prefer":30,"year":6},
{"value":1,"name":"Martin","prefer":40,"year":7},
{"value":2,"name":"Adam","prefer":0,"year":20},
{"value":2,"name":"Steve","prefer":100,"year":5}
]
const values = []
const names = []
const prefers = []
const years = []
array.forEach(rec => {
values.push(rec.value),
names.push(rec.name),
prefers.push(rec.prefer),
years.push(rec.year)
})
console.log(values)
console.log(names)
console.log(prefers)
console.log(years)
Map should work:
const data = [
{ value: 1, name: "Fiona", prefer: 30, year: 6 },
{ value: 1, name: "Martin", prefer: 40, year: 7 },
{ value: 2, name: "Adam", prefer: 0, year: 20 },
{ value: 2, name: "Steve", prefer: 100, year: 5 },
];
const values = data.map(x=>x.value);
const names = data.map(x=>x.name);
console.log(values, names);
//[ 1, 1, 2, 2 ] [ 'Fiona', 'Martin', 'Adam', 'Steve' ]
See MDN for details of map
You could also make it a little more dynamic by using reduce and then only getting the lists you want using Object destructuring.
const arr = [
{"value":1,"name":"Fiona","prefer":30,"year":6},
{"value":1,"name":"Martin","prefer":40,"year":7},
{"value":2,"name":"Adam","prefer":0,"year":20},
{"value":2,"name":"Steve","prefer":100,"year":5}
];
const {name, value, prefer, year} = arr.reduce((acc, curr) => {
Object.entries(curr).forEach(([key, val]) => {
if(acc[key] == null)
acc[key] = [];
acc[key].push(val);
});
return acc;
}, {})
console.log(name);
console.log(value);
console.log(prefer);
console.log(year);
I am trying to take this array :
[
{
"date": a timestamp: June 20, 2020 at 7:32:42 PM UTC
"value": 3
..
}
..
]
and accomplish 3 things effeciently
Convert the timestamp to normal date and replace with the timestamp
Merge dates of the same day. ( so i add their values and set under a single day.
Sort the array when recent are first.
I have started with something like this :
array.sort(function(a,b){ return new Date(b.date) - new Date(a.date);
var salesDates = sales.map(function(element){element.date = new Date(element.date); return element }); });
Which will only sort, but i need to replace timestamp/date, sort and merge same dates elegantly and effeciently
Is it possible with only sort function ?
Here. First i group it with .reduce(). Then i sort it .sort(). After that i change the timestamp to a date format.
let arr = [
{
"date": 1594023899426,
"value": 3
},
{
"date": 1592423499234,
"value": 2
},
{
"date": 1594023899426,
"value": 1
}
];
let result = arr
.reduce((a, v) => {
let index = a.findIndex(el => el.date === v.date);
if (index !== -1) {
a[index].value += v.value;
return a;
}
a.push({
date: v.date,
value: v.value
});
return a;
}, [])
.sort((a, b) => b.date - a.date)
.map(el => {
el.date = new Date(el.date);
return el;
});
console.log(result);
Here's another approach to it:
let sortingReducer = (accumulator, obj) => {
// This is the merging logic
let existingObj = accumulator.find(
(compareObj) => {
return obj.date?.getDate() === compareObj.date?.getDate()
}
);
if (existingObj) {
existingObj.value += obj.value;
return accumulator;
}
// This is the sorting logic
const nextIndex = accumulator.findIndex(
(compareObj) => obj.date?.getTime() < compareObj.date?.getTime()
);
const index = nextIndex > -1 ? nextIndex : accumulator.length;
accumulator.splice(index, 0, obj);
return accumulator;
};
const input = [
{
date: new Date(),
value: 2,
},
{
date: new Date(new Date().setDate(1)),
value: 4,
},
{
date: new Date(new Date().setDate(1)),
value: 1,
},
{
date: new Date(new Date().setDate(2)),
value: 7,
},
];
const output = input.reduce(sortingReducer, []);
I've take some help from this answer: https://stackoverflow.com/a/50246275/1912288
I like the approach in the above answer, this is just a different approach to it.
I need to get all unique days of multiple date values in the format DD.MM.. In this example data, there are two values for the 24th of december:
const data = [
{ date: ISODate("2019-12-24T03:24:00Z") },
{ date: ISODate("2019-12-24T04:56:00Z") },
{ date: ISODate("2019-12-25T02:34:00Z") },
{ date: ISODate("2019-12-26T01:23:00Z") }
]
So the result should be
const result = [
'24.12.',
'25.12.',
'26.12.'
]
So first of all I'll map my data and split the values only for the dates:
const dates = data.map(d => d.date.toString().split('T')[0])
But how do I get the unique values and change the output format?
Update
I came up with this, but it looks very complicated...
data.map(d => {
const dateSplit = d.date.toString().split('T')[0].split('-')
return dateSplit[2] + '.' + dateSplit[1] + '.'
})
.filter((value, index, self) {
return self.indexOf(value) === index
})
It seems that ISODate returns a standard JS Date object. You can use Date.getDate() to get the day, and Date.getMonth() to get the month (0 based, so we need to add 1):
const data = [
{ date: new Date('2019-12-24T03:24:00Z') },
{ date: new Date('2019-12-24T04:56:00Z') },
{ date: new Date('2019-12-25T02:34:00Z') },
{ date: new Date('2019-12-26T01:23:00Z') }
]
const result = [...new Set(data.map(({ date: d }) =>
`${d.getDate()}.${d.getMonth() + 1}.`
))]
console.log(result)
Previous answer:
Use a regular expression to match the month and the day, and assign them to consts using destructuring. Assemble the string using template literal. Remove duplicates by assigning the values to a Set, and then spreading back to an array.
Note: Since I don't have access to the ISODate, I've removed it. I left .toString() although it's not needed in this example, but will be needed when used with ISODate.
const data = [
{ date: '2019-12-24T03:24:00Z' },
{ date: '2019-12-24T04:56:00Z' },
{ date: '2019-12-25T02:34:00Z' },
{ date: '2019-12-26T01:23:00Z' }
]
const pattern = /-([0-9]{2})-([0-9]{2})T/
const result = [...new Set(data.map(d => {
const [, mon, day] = d.date.toString().match(pattern)
return `${day}.${mon}.`;
}))]
console.log(result)
Use .filter() to filter through only values that are the first of their value.
//temporary function
const ISODate = (d) => d;
const data = [{
date: ISODate("2019-12-24T03:24:00Z")
},
{
date: ISODate("2019-12-24T04:56:00Z")
},
{
date: ISODate("2019-12-25T02:34:00Z")
},
{
date: ISODate("2019-12-26T01:23:00Z")
}
]
const dates = data.map(d => d.date.toString().split('T')[0].split("-").slice(1, 3).reverse().join(".") + ".")
console.log(dates.filter((v, i, a) => a.indexOf(v) === i));
You can do this pretty easily by using Array.reduce. Note that I converted ISODate to be Date since I don't have that class, but it should be the same concept.
const data = [
{ date: new Date("2019-12-24T03:24:00Z") },
{ date: new Date("2019-12-24T04:56:00Z") },
{ date: new Date("2019-12-25T02:34:00Z") },
{ date: new Date("2019-12-26T01:23:00Z") }
];
const result = data.reduce( (acc, curr) => {
if (acc.length > 0) {
const hasDate = acc.find(d => d.date.getMonth() === curr.date.getMonth() && d.date.getDate() === curr.date.getDate());
if (!hasDate) { acc.push(curr); }
} else {
acc.push(curr);
}
return acc;
}, []);
console.log(result);
I would use the uniq function in the Underscore.js library:
const data = [
{ date: ISODate("2019-12-24T03:24:00Z") },
{ date: ISODate("2019-12-24T04:56:00Z") },
{ date: ISODate("2019-12-25T02:34:00Z") },
{ date: ISODate("2019-12-26T01:23:00Z") }
];
let dates = _.uniq(data.map(d => d.date.toString().split('T')[0]));
A nice considerable way is:
const array = [1, 2, 6, 5,5, 5, 3, 7, 8];
const uniqueKeys = array.reduce((hashMap, value) => {
if (!hashMap[value]) {
hashMap[value] = true;
}
return hashMap;
}, {});
const uniqueValues = Object.keys(uniqueKeys);
console.log(uniqueValues);
It is nice because it iterates the array once, instead of x * x (a.k.a log(n) instead of log(n^2) as with .filter() example
const array = [1, 2, 6, 5,5, 5, 3, 7, 8];
const uniqueKeys = array.reduce((hashMap, value) => {
if (!hashMap[value]) {
hashMap[value] = true;
}
return hashMap;
}, {});
const uniqueValues = Object.keys(uniqueKeys);
console.log(uniqueValues);
how to count the value of object in new object values
lets say that i have json like this :
let data = [{
no: 3,
name: 'drink'
},
{
no: 90,
name: 'eat'
},
{
no: 20,
name: 'swim'
}
];
if i have the user pick no in arrays : [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
so the output should be an array
[
{
num: 3,
total: 11
},
{
num: 90,
total: 1
},
{
num:20,
total: 4
}
];
I would like to know how to do this with a for/of loop
Here is the code I've attempted:
let obj = [];
for (i of arr){
for (j of data){
let innerObj={};
innerObj.num = i
obj.push(innerObj)
}
}
const data = [{"no":3,"name":"drink"},{"no":90,"name":"eat"},{"no":20,"name":"swim"}];
const arr = [3,3,3,3,3,3,3,3,3,3,3,20,20,20,20,80,80];
const lookup = {};
// Loop over the duplicate array and create an
// object that contains the totals
for (let el of arr) {
// If the key doesn't exist set it to zero,
// otherwise add 1 to it
lookup[el] = (lookup[el] || 0) + 1;
}
const out = [];
// Then loop over the data updating the objects
// with the totals found in the lookup object
for (let obj of data) {
lookup[obj.no] && out.push({
no: obj.no,
total: lookup[obj.no]
});
}
document.querySelector('#lookup').textContent = JSON.stringify(lookup, null, 2);
document.querySelector('#out').textContent = JSON.stringify(out, null, 2);
<h3>Lookup output</h3>
<pre id="lookup"></pre>
<h3>Main output</h3>
<pre id="out"></pre>
Perhaps something like this? You can map the existing data array and attach filtered array counts to each array object.
let data = [
{
no: 3,
name: 'drink'
},
{
no:90,
name: 'eat'
},
{
no:20,
name: 'swim'
}
]
const test = [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
const result = data.map((item) => {
return {
num: item.no,
total: test.filter(i => i === item.no).length // filters number array and then checks length
}
})
You can check next approach using a single for/of loop. But first I have to create a Set with valid ids, so I can discard noise data from the test array:
const data = [
{no: 3, name: 'drink'},
{no: 90, name: 'eat'},
{no: 20, name: 'swim'}
];
const userArr = [3,3,3,3,3,3,3,3,7,7,9,9,3,3,3,90,20,20,20,20];
let ids = new Set(data.map(x => x.no));
let newArr = [];
for (i of userArr)
{
let found = newArr.findIndex(x => x.num === i)
if (found >= 0)
newArr[found].total += 1;
else
ids.has(i) && newArr.push({num: i, total: 1});
}
console.log(newArr);