Group array of objects by two properties - javascript

I'm trying to group array of objects by two properties so I get data on two levels. This is in JavaScript - Node server.
Here is the data I'm starting with
items = [
{month: 11, year: 2022, amount: 10},
{month: 12, year: 2022, amount: 6},
{month: 11, year: 2022, amount: 7},
{month: 12, year: 2022, amount: 12},
{month: 1, year: 2023, amount: 5},
{month: 1, year: 2023, amount: 15},
{month: 11, year: 2023, amount: 30},
{month: 11, year: 2023, amount: 20}
],
I need all items from the same month and in the same year grouped together. The final result would look like this:
result = {
"2022": {
"11": [
{month: 11, year: 2022, amount: 10},
{month: 11, year: 2022, amount: 7}
],
"12": [
{month: 12, year: 2022, amount: 6},
{month: 12, year: 2022, amount: 12}
]
},
"2023": {
"11": [
{month: 11, year: 2023, amount: 30},
{month: 11, year: 2023, amount: 20}
],
"1": [
{month: 1, year: 2023, amount: 5},
{month: 1, year: 2023, amount: 15}
]
}
}
The first step is done quite easily, either through reduce or groupBy(), I've opted for groupBy() because it is cleaner:
const itemsPerYear = items.groupBy(item => { return item.year })
This gives me intermediate result:
itemsPerYear = {
"2022": [
{month: 11, year: 2022, amount: 10},
{month: 11, year: 2022, amount: 7},
{month: 12, year: 2022, amount: 6},
{month: 12, year: 2022, amount: 12}
],
"2023": [
{month: 11, year: 2023, amount: 30},
{month: 11, year: 2023, amount: 20},
{month: 1, year: 2023, amount: 5},
{month: 1, year: 2023, amount: 15}
]
}
So if I apply similar logic and go with:
const itemsPerMonth = Object.values(itemsPerYear).groupBy(item => { return item.month })
I get:
[Object: null prototype] {
undefined: [
[ [Object], [Object], [Object], [Object] ],
[ [Object], [Object], [Object], [Object] ]
]
}
I get a step closer with ForEach:
const itemsPerMonth = Object.values(itemsPerYear).forEach(sub => { console.log(sub) })
I get:
[
{month: 11, year: 2022, amount: 10},
{month: 11, year: 2022, amount: 7},
{month: 12, year: 2022, amount: 6},
{month: 12, year: 2022, amount: 12}
],
[
{month: 11, year: 2023, amount: 30},
{month: 11, year: 2023, amount: 20},
{month: 1, year: 2023, amount: 5},
{month: 1, year: 2023, amount: 15}
]
If I want to use groupBy() inside the ForEach() I get undefined.
Thanks

Here is a quick solution using a .reduce():
const input = [ {month: 11, year: 2022, amount: 10}, {month: 12, year: 2022, amount: 6}, {month: 11, year: 2022, amount: 7}, {month: 12, year: 2022, amount: 12}, {month: 1, year: 2023, amount: 5}, {month: 1, year: 2023, amount: 15}, {month: 11, year: 2023, amount: 30}, {month: 11, year: 2023, amount: 20} ];
const result = input.reduce((acc, obj) => {
if(!acc[obj.year]) acc[obj.year] = {};
if(!acc[obj.year][obj.month]) acc[obj.year][obj.month] = [];
acc[obj.year][obj.month].push(obj);
return acc;
}, {});
console.log('result:', result);
Docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

Related

How to populate dependable values into dropdown using javascript

I am trying to make a car year, make, model dependable values form. My arrays are in the following format. For the selected year and selected make, i want to populate the corresponding model value.
I have put unique year and make into separate another array. How will i achieve the intended result?
var car_arrays = [
{
id: 1,
year: 1909,
make: "Ford",
model: "Model T",
},
{
id: 2,
year: 1926,
make: "Chrysler",
model: "Imperial",
},
{
id: 3,
year: 1948,
make: "Citroën",
model: "2CV",
},
{
id: 4,
year: 1950,
make: "Hillman",
model: "Minx Magnificent",
},
{
id: 5,
year: 1953,
make: "Chevrolet",
model: "Corvette",
},
{
id: 7,
year: 1954,
make: "Cadillac",
model: "Fleetwood",
},
{
id: 6,
year: 1954,
make: "Chevrolet",
model: "Corvette",
},
{
id: 8,
year: 1955,
make: "Chevrolet",
model: "Corvette",
},
{
id: 9,
year: 1955,
make: "Ford",
model: "Thunderbird",
},
{
id: 10,
year: 1956,
make: "Chevrolet",
model: "Corvette",
},
{
id: 12,
year: 1957,
make: "BMW",
model: "600",
},
{
id: 11,
year: 1957,
make: "Chevrolet",
model: "Corvette",
},
{
id: 14,
year: 1958,
make: "BMW",
model: "600",
},
{
id: 13,
year: 1958,
make: "Chevrolet",
model: "Corvette",
},
{
id: 15,
year: 1958,
make: "Ford",
model: "Thunderbird",
},
{
id: 16,
year: 1959,
make: "Austin",
model: "Mini",
},
{
id: 18,
year: 1959,
make: "BMW",
model: "600",
},
{
id: 17,
year: 1959,
make: "Chevrolet",
model: "Corvette",
},
{
id: 19,
year: 1960,
make: "Chevrolet",
model: "Corvair",
},
{
id: 20,
year: 1960,
make: "Chevrolet",
model: "Corvette",
},
{
id: 22,
year: 1960,
make: "Fairthorpe",
model: "Rockette",
},
{
id: 21,
year: 1960,
make: "Fillmore",
model: "Fillmore",
},
{
id: 23,
year: 1961,
make: "Austin",
model: "Mini Cooper",
},
{
id: 26,
year: 1961,
make: "Chevrolet",
model: "Corvette",
},
{
id: 25,
year: 1961,
make: "Pontiac",
model: "Tempest",
},
{
id: 24,
year: 1961,
make: "Studebaker",
model: "Avanti",
},
{
id: 30,
year: 1962,
make: "Buick",
model: "Special",
},
{
id: 28,
year: 1962,
make: "Chevrolet",
model: "Corvette",
},
{
id: 27,
year: 1962,
make: "Pontiac",
model: "Grand Prix",
},
{
id: 29,
year: 1962,
make: "Studebaker",
model: "Avanti",
},
{
id: 31,
year: 1963,
make: "Austin",
model: "Mini",
},
{
id: 32,
year: 1963,
make: "Austin",
model: "Mini Cooper S",
},
{
id: 37,
year: 1963,
make: "Chevrolet",
model: "Corvair 500",
},
{
id: 38,
year: 1963,
make: "Chevrolet",
model: "Corvette",
},
{
id: 34,
year: 1963,
make: "Ford",
model: "E-Series",
},
{
id: 36,
year: 1963,
make: "Pontiac",
model: "Grand Prix",
},
{
id: 33,
year: 1963,
make: "Rambler",
model: "Classic",
},];
function getYear() {
var makeid = document.getElementById("makeid");
var select, option;
select = document.getElementById("yearid");
for (let y in year) {
option = document.createElement("option");
option.value = option.text = year[y];
select.add(option);
}
var selectyear = document.getElementById("yearid");
makeid.removeAttribute("disabled");
}
function getMake() {
var makeid, yearid, option;
makeidselect = document.getElementById("makeid");
yearidselect = document.getElementById("yearid");
for (let y in year) {
option = document.createElement("option");
option.value = option.text = year[y];
yearidselect.add(option);
console.log(yearidselect.value);
}
for (let x in make) {
option = document.createElement("option");
option.value = option.text = make[x];
makeidselect.add(option);
console.log(makeidselect.value);
}
var year = [
1909, 1926, 1948, 1950, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960, 1961,
1962, 1963, 1964, 1965, 1966, 1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974,
1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987,
1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013,
2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021,
];
var make = [
"Ford",
"Chrysler",
"Citroën",
"Hillman",
"Chevrolet",
"Cadillac",
"BMW",
"Austin",
"Fairthorpe",
"Fillmore",
"Pontiac",
"Studebaker",
"Buick",
"Rambler",
"Plymouth",
"Volkswagen",
"Jensen",
"Oldsmobile",
"Mercury",
"Dodge",
"Shelby",
"Porsche",
"Toyota",
"Mercedes-Benz",
"MG",
"Nissan",
"Honda",
"Mazda",
"Renault",
"Audi",
"Lincoln",
"Lotus",
"Maserati",
"Mitsubishi",
"Saab",
"Subaru",
"Suzuki",
"Lamborghini",
"Merkur",
"Land Rover",
"Acura",
"Lexus",
"Eagle",
"Alfa Romeo",
"Daihatsu",
"Geo",
"GMC",
"Hyundai",
"Infiniti",
"Isuzu",
"Jaguar",
"Jeep",
"Saturn",
"Volvo",
"HUMMER",
"Kia",
"Holden",
"Corbin",
"Daewoo",
"MINI",
"Maybach",
"Scion",
"Spyker",
"Aston Martin",
"Bentley",
"Panoz",
"Rolls-Royce",
"Spyker Cars",
"Ferrari",
"Hummer",
"Morgan",
"Peugeot",
"Foose",
"Aptera",
"Smart",
"Bugatti",
"Tesla",
"Ram",
"FIAT",
"Fiat",
"McLaren",
"BYD",
"McLaren Automotive",
"Mobility Ventures LLC",
"Pagani",
"Roush Performance",
"smart",
"SRT",
"Genesis",
"Karma",
"Koenigsegg",
"RUF Automobile",
"STI",
"Polestar",
"Kandi",
];
You can do something like this to populate the models dropdown
according to the values of year and make:
var CARS = [{
id: 1,
year: 1909,
make: 'Ford',
model: 'Model T',
},
{
id: 2,
year: 1926,
make: 'Chrysler',
model: 'Imperial',
},
{
id: 3,
year: 1948,
make: 'Citroën',
model: '2CV',
},
{
id: 4,
year: 1950,
make: 'Hillman',
model: 'Minx Magnificent',
},
{
id: 5,
year: 1953,
make: 'Chevrolet',
model: 'Corvette',
},
{
id: 7,
year: 1954,
make: 'Cadillac',
model: 'Fleetwood',
},
{
id: 6,
year: 1954,
make: 'Chevrolet',
model: 'Corvette',
},
{
id: 8,
year: 1955,
make: 'Chevrolet',
model: 'Corvette',
},
{
id: 9,
year: 1955,
make: 'Ford',
model: 'Thunderbird',
},
{
id: 10,
year: 1956,
make: 'Chevrolet',
model: 'Corvette',
},
{
id: 12,
year: 1957,
make: 'BMW',
model: '600',
},
{
id: 11,
year: 1957,
make: 'Chevrolet',
model: 'Corvette',
},
{
id: 14,
year: 1958,
make: 'BMW',
model: '600',
},
{
id: 13,
year: 1958,
make: 'Chevrolet',
model: 'Corvette',
},
{
id: 15,
year: 1958,
make: 'Ford',
model: 'Thunderbird',
},
{
id: 16,
year: 1959,
make: 'Austin',
model: 'Mini',
},
{
id: 18,
year: 1959,
make: 'BMW',
model: '600',
},
{
id: 17,
year: 1959,
make: 'Chevrolet',
model: 'Corvette',
},
{
id: 19,
year: 1960,
make: 'Chevrolet',
model: 'Corvair',
},
{
id: 20,
year: 1960,
make: 'Chevrolet',
model: 'Corvette',
},
{
id: 22,
year: 1960,
make: 'Fairthorpe',
model: 'Rockette',
},
{
id: 21,
year: 1960,
make: 'Fillmore',
model: 'Fillmore',
},
{
id: 23,
year: 1961,
make: 'Austin',
model: 'Mini Cooper',
},
{
id: 26,
year: 1961,
make: 'Chevrolet',
model: 'Corvette',
},
{
id: 25,
year: 1961,
make: 'Pontiac',
model: 'Tempest',
},
{
id: 24,
year: 1961,
make: 'Studebaker',
model: 'Avanti',
},
{
id: 30,
year: 1962,
make: 'Buick',
model: 'Special',
},
{
id: 28,
year: 1962,
make: 'Chevrolet',
model: 'Corvette',
},
{
id: 27,
year: 1962,
make: 'Pontiac',
model: 'Grand Prix',
},
{
id: 29,
year: 1962,
make: 'Studebaker',
model: 'Avanti',
},
{
id: 31,
year: 1963,
make: 'Austin',
model: 'Mini',
},
{
id: 32,
year: 1963,
make: 'Austin',
model: 'Mini Cooper S',
},
{
id: 37,
year: 1963,
make: 'Chevrolet',
model: 'Corvair 500',
},
{
id: 38,
year: 1963,
make: 'Chevrolet',
model: 'Corvette',
},
{
id: 34,
year: 1963,
make: 'Ford',
model: 'E-Series',
},
{
id: 36,
year: 1963,
make: 'Pontiac',
model: 'Grand Prix',
},
{
id: 33,
year: 1963,
make: 'Rambler',
model: 'Classic',
},
]
const YEARS = [
1909, 1926, 1948, 1950, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960, 1961,
1962, 1963, 1964, 1965, 1966, 1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974,
1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987,
1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013,
2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021,
]
const MAKES = [
'Ford',
'Chrysler',
'Citroën',
'Hillman',
'Chevrolet',
'Cadillac',
'BMW',
'Austin',
'Fairthorpe',
'Fillmore',
'Pontiac',
'Studebaker',
'Buick',
'Rambler',
'Plymouth',
'Volkswagen',
'Jensen',
'Oldsmobile',
'Mercury',
'Dodge',
'Shelby',
'Porsche',
'Toyota',
'Mercedes-Benz',
'MG',
'Nissan',
'Honda',
'Mazda',
'Renault',
'Audi',
'Lincoln',
'Lotus',
'Maserati',
'Mitsubishi',
'Saab',
'Subaru',
'Suzuki',
'Lamborghini',
'Merkur',
'Land Rover',
'Acura',
'Lexus',
'Eagle',
'Alfa Romeo',
'Daihatsu',
'Geo',
'GMC',
'Hyundai',
'Infiniti',
'Isuzu',
'Jaguar',
'Jeep',
'Saturn',
'Volvo',
'HUMMER',
'Kia',
'Holden',
'Corbin',
'Daewoo',
'MINI',
'Maybach',
'Scion',
'Spyker',
'Aston Martin',
'Bentley',
'Panoz',
'Rolls-Royce',
'Spyker Cars',
'Ferrari',
'Hummer',
'Morgan',
'Peugeot',
'Foose',
'Aptera',
'Smart',
'Bugatti',
'Tesla',
'Ram',
'FIAT',
'Fiat',
'McLaren',
'BYD',
'McLaren Automotive',
'Mobility Ventures LLC',
'Pagani',
'Roush Performance',
'smart',
'SRT',
'Genesis',
'Karma',
'Koenigsegg',
'RUF Automobile',
'STI',
'Polestar',
'Kandi',
]
MAKES.sort()
const yearSelection = document.getElementById('year')
const makeSelection = document.getElementById('make')
const modelSelection = document.getElementById('model')
const displayedCars = document.getElementById('cars')
for (let year of YEARS) {
const option = document.createElement('option')
option.textContent = year
option.value = year
yearSelection.appendChild(option)
}
for (let make of MAKES) {
const option = document.createElement('option')
option.textContent = make
option.value = make
makeSelection.appendChild(option)
}
const models = []
for (let car of CARS) {
const model = car.model
if (models.findIndex(m => m == model) == -1) {
const option = document.createElement('option')
option.className = 'car-model'
models.push(model)
option.textContent = model
option.value = model
modelSelection.appendChild(option)
}
}
const modelOptions = Object.values(document.querySelectorAll('.car-model'))
function populateModels() {
const selectedYear = yearSelection.value
const selectedMake = makeSelection.value
const filtered = CARS.filter(
car => car.year == selectedYear && car.make == selectedMake
)
for (let option of modelOptions) {
if (filtered.find(v => v.model == option.value)) {
option.hidden = false
} else {
option.hidden = true
}
}
if (filtered.length > 0) {
modelSelection.value = filtered[0].model
} else {
modelSelection.value = 'none'
}
}
populateModels()
yearSelection.onchange = () => populateModels()
makeSelection.onchange = () => populateModels()
<!DOCTYPE html>
<html>
<head>
<link rel="shortcut icon" href="#" type="image/x-icon" />
<meta charset="UTF-8" />
</head>
<body>
<select name="" id="year"></select>
<select name="" id="make"></select>
<select name="" id="model">
<option value="none" disabled hidden>No models found</option>
</select>
</body>
<script src="main.js"></script>
</html>
you can loop through the array:
let year = [], let make = [], ....
car_arrays.forEach(car => {
year.push(car.year)
make.push(car.make) ...
})

React table - format data to create subRows

I'm using react-table to create an expandible table.
In that example data are in this format:
[
{
"firstName": "wheel",
"lastName": "arch",
"age": 29,
"visits": 39,
"progress": 87,
"status": "single",
"subRows": [
{
"firstName": "singer",
"lastName": "paper",
"age": 24,
"visits": 35,
"progress": 55,
"status": "complicated",
"subRows": [
{
"firstName": "professor",
"lastName": "beam",
"age": 22,
"visits": 14,
"progress": 84,
"status": "single"
}, {...}
]
},
]
}
{
"firstName": "elevator",
"lastName": "contribution",
"age": 26,
"visits": 74,
"progress": 28,
"status": "relationship",
"subRows": [
{
"firstName": "debt",
"lastName": "honey",
"age": 3,
"visits": 31,
"progress": 31,
"status": "relationship"
}, {...}
]
},
{
"firstName": "cup",
"lastName": "media",
"age": 8,
"visits": 77,
"progress": 37,
"status": "single",
"subRows": undefined
}
]
So an array of objects and each object has a property subRows that can be undefinedd or an array of objects.
I've a flat dataset, so an array of objects and I want to recreate the above structure grouping by a property.
For example, this is my dataset:
const data = [
{animal: 'cat', name: 'mu', year: 2016},
{animal: 'cat', name: 'muji', year: 2021},
{animal: 'cat', name: 'mine', year: 2021},
{animal: 'dog', name: 'fido', year: 2000},
{animal: 'hamster', name: 'gerry', year: 2020},
{animal: 't-rex', name: 'dino', year: 2020},
{animal: 'sheep', name: 's', year: 2019},
{animal: 'sheep', name: 'sss', year: 2016},
]
and I would like that the animal column is expandible. How can I do?
I try groupBy by Lodash but obviously it's not the good method here.
const result = [
{animal: 'cat', name: 'mu', year: 2016},
{animal: 'cat', name: 'muji', year: 2021},
{animal: 'cat', name: 'mine', year: 2021},
{animal: 'dog', name: 'fido', year: 2000},
{animal: 'hamster', name: 'gerry', year: 2020},
{animal: 't-rex', name: 'dino', year: 2020},
{animal: 'sheep', name: 's', year: 2019},
{animal: 'sheep', name: 'sss', year: 2016},
].reduce(function (acc, obj) {
const x = acc.findIndex((entity)=>entity.animal===obj.animal);
if(x!== -1){
acc[x].subrows.push(obj)
}else{
const xx= {'animal':obj.animal,subrows:[obj]}
acc.push(xx)
}
return acc;
}, [])
console.log(result)

Javascript data wrangling: Calculate percent change in a nested json

Having a json file in this format...
data =[
{
key: "london",
values: [
{day: "2020-01-01", city: "london", value: 10},
{day: "2020-01-02", city: "london", value: 20},
{day: "2020-01-03", city: "london", value: 30},
{day: "2020-01-04", city: "london", value: 30},
{day: "2020-01-05", city: "london", value: 30},
{day: "2020-01-06", city: "london", value: 30}
]
},
{
key: "berlin",
values: [
{day: "2020-01-01", city: "berlin", value: 10},
{day: "2020-01-02", city: "berlin", value: 15},
{day: "2020-01-03", city: "berlin", value: 30},
{day: "2020-01-04", city: "berlin", value: 30},
{day: "2020-01-05", city: "berlin", value: 30},
{day: "2020-01-06", city: "berlin", value: 45}
]
},
{
key: "rome",
values: [
{day: "2020-01-01", city: "rome", value: 10},
{day: "2020-01-02", city: "rome", value: 12},
{day: "2020-01-03", city: "rome", value: 6},
{day: "2020-01-04", city: "rome", value: 9},
{day: "2020-01-05", city: "rome", value: 27},
{day: "2020-01-06", city: "rome", value: 36}
]
}]
I was wondering how I can calculate the daily percentage change in the series using javascript. I am expecting to get the following output. If possible, I'd like to remove city in order not to repeat information.
data =[
{
key: "london",
values: [
{day: "2020-01-01", value: 10, perc: 0},
{day: "2020-01-02", value: 20, perc: 1},
{day: "2020-01-03", value: 30, perc: 1},
{day: "2020-01-04", value: 30, perc: 0},
{day: "2020-01-05", value: 30, perc: 0},
{day: "2020-01-06", value: 30, perc: 0}
]
},
{
key: "berlin",
values: [
{day: "2020-01-01", value: 10, perc: 0},
{day: "2020-01-02", value: 15, perc: 0.5},
{day: "2020-01-03", value: 30, perc: 1},
{day: "2020-01-04", value: 30, perc: 0},
{day: "2020-01-05", value: 30, perc: 0},
{day: "2020-01-06", value: 45, perc: 0.5}
]
},
{
key: "rome",
values: [
{day: "2020-01-01", value: 10, perc: 0},
{day: "2020-01-02", value: 12, perc: 0.2},
{day: "2020-01-03", value: 6, perc: -0.5},
{day: "2020-01-04", value: 9, perc: 0.5},
{day: "2020-01-05", value: 27, perc: 2},
{day: "2020-01-06", value: 36, perc: 0.33}
]
}]
Bonus question: What should I do in order to calculate percentage change for a different period of time (every two days, week, etc.) and get an output like the one below? (Showing percentage change every two days)
data =[
{
key: "london",
values: [
{day: "2020-01-01", value: 10, perc: 0},
{day: "2020-01-03", value: 30, perc: 2},
{day: "2020-01-05", value: 30, perc: 0},
]
},
{
key: "berlin",
values: [
{day: "2020-01-01", value: 10, perc: 0},
{day: "2020-01-03", value: 30, perc: 2},
{day: "2020-01-05", value: 30, perc: 0},
]
},
{
key: "rome",
values: [
{day: "2020-01-01", value: 10, perc: 0},
{day: "2020-01-03", value: 6, perc: -0.4},
{day: "2020-01-05", value: 27, perc: 4.5},
]
}]
Here's my take on this situation. It also includes the bonus question.
P.S.: This is my first stackoverflow post, if you have any questions please ask!
// Your input data
const data = [
{
key: "london",
values: [
{day: "2020-01-01", city: "london", value: 10},
{day: "2020-01-02", city: "london", value: 20},
{day: "2020-01-03", city: "london", value: 30},
{day: "2020-01-04", city: "london", value: 30},
{day: "2020-01-05", city: "london", value: 30},
{day: "2020-01-06", city: "london", value: 30}
]
},
{
key: "berlin",
values: [
{day: "2020-01-01", city: "berlin", value: 10},
{day: "2020-01-02", city: "berlin", value: 15},
{day: "2020-01-03", city: "berlin", value: 30},
{day: "2020-01-04", city: "berlin", value: 30},
{day: "2020-01-05", city: "berlin", value: 30},
{day: "2020-01-06", city: "berlin", value: 45}
]
},
{
key: "rome",
values: [
{day: "2020-01-01", city: "rome", value: 10},
{day: "2020-01-02", city: "rome", value: 12},
{day: "2020-01-03", city: "rome", value: 6},
{day: "2020-01-04", city: "rome", value: 9},
{day: "2020-01-05", city: "rome", value: 27},
{day: "2020-01-06", city: "rome", value: 36}
]
}];
// The parsed output data
const parsedData = data.map((obj) => {
// Maps the object values to calculate the percentage
return {...obj, values: obj.values.map((value, i) => {
// Delete the "city" key
delete value['city'];
// Can't calculate the percentage on the first element
if (i == 0)
return { ...value, perc: 0 };
// Get the current & previous day/value
const currentValue = value.value;
const currentDay = new Date(value.day);
const previousValue = obj.values[i-1].value;
const previousDay = new Date(obj.values[i-1].day);
// Calculate the days between the previous and current entry
const dayTimeDiff = Math.abs(currentDay - previousDay);
const dayDiff = Math.ceil(dayTimeDiff / (1000 * 60 * 60 * 24));
// Calculate the precentage = (current - previous) / previous
const percentDiff = currentValue - previousValue / previousValue / dayDiff;
return { ...value, perc: percentDiff };
})}
});
console.log(parsedData);
For the first part, you can manipulate each value inside values array:
const data = [
{
key: "london",
values: [
{day: "2020-01-01", city: "london", value: 10},
{day: "2020-01-02", city: "london", value: 20},
{day: "2020-01-03", city: "london", value: 30},
{day: "2020-01-04", city: "london", value: 30},
{day: "2020-01-05", city: "london", value: 30},
{day: "2020-01-06", city: "london", value: 30}
]
},
{
key: "berlin",
values: [
{day: "2020-01-01", city: "berlin", value: 10},
{day: "2020-01-02", city: "berlin", value: 15},
{day: "2020-01-03", city: "berlin", value: 30},
{day: "2020-01-04", city: "berlin", value: 30},
{day: "2020-01-05", city: "berlin", value: 30},
{day: "2020-01-06", city: "berlin", value: 45}
]
},
{
key: "rome",
values: [
{day: "2020-01-01", city: "rome", value: 10},
{day: "2020-01-02", city: "rome", value: 12},
{day: "2020-01-03", city: "rome", value: 6},
{day: "2020-01-04", city: "rome", value: 9},
{day: "2020-01-05", city: "rome", value: 27},
{day: "2020-01-06", city: "rome", value: 36}
]
}]
const newData = data.map((info) => {
const n = info.values.length;
const valuesCopy = info.values.map((info) => info.value);
for (let i = 0; i < n; i++) {
const currentValue = valuesCopy[i];
const previousValue = valuesCopy[i - 1];
// calculate the percentage
const percentage = (currentValue - previousValue) / previousValue;
// percentage is NaN return 0
// percentage is < 1, return 2 decimal places
// otherwise return percentage
info.values[i].value = !percentage ? 0 : percentage < 1 ? percentage.toFixed(2) : percentage;
}
return info;
})
console.log(JSON.stringify(newData, null, 2));
Example using simple for loops + comments:
const data =[
{
key: "london",
values: [
{day: "2020-01-01", city: "london", value: 10},
{day: "2020-01-02", city: "london", value: 20},
{day: "2020-01-03", city: "london", value: 30},
{day: "2020-01-04", city: "london", value: 30},
{day: "2020-01-05", city: "london", value: 30},
{day: "2020-01-06", city: "london", value: 30}
]
},
{
key: "berlin",
values: [
{day: "2020-01-01", city: "berlin", value: 10},
{day: "2020-01-02", city: "berlin", value: 15},
{day: "2020-01-03", city: "berlin", value: 30},
{day: "2020-01-04", city: "berlin", value: 30},
{day: "2020-01-05", city: "berlin", value: 30},
{day: "2020-01-06", city: "berlin", value: 45}
]
},
{
key: "rome",
values: [
{day: "2020-01-01", city: "rome", value: 10},
{day: "2020-01-02", city: "rome", value: 12},
{day: "2020-01-03", city: "rome", value: 6},
{day: "2020-01-04", city: "rome", value: 9},
{day: "2020-01-05", city: "rome", value: 27},
{day: "2020-01-06", city: "rome", value: 36}
]
}
];
// Define resulting array
const results = [];
// Loop data
for(let i = 0; i < data.length; i++) {
// Set city object
const city = {
key: data[i].key,
values: []
};
// Set shorcut to values
const dVal = data[i].values
// Loop values in city entry
for(let i = 0; i < dVal.length; i++) {
// Set previous value or current if it is first cycle
const prev = (i !== 0) ? dVal[i-1].value : dVal[i].value;
// Set current value
const cur = dVal[i].value;
// Calculate percentage
let percDiff = (cur - prev) / prev;
// Fancy result as you need
percDiff = parseFloat(percDiff.toFixed(2));
// Push to city object
city.values.push({
day: dVal[i].day,
value: dVal[i].value,
perc: percDiff
});
}
// Push city object to resulting array
results.push(city);
}
// Log
console.log(results);
Answer on your second question, if I get it right, is simple - remove day entries that you don't need from array and pass resulting array to the same function. It calculates difference between entries, doesn't matter it is day or week

Is there a more efficient way to sort an Array in an Array in an Object?

For the following array-object-thing structure:
Events : {
events : [
{
startDTG : {day: 0, month: 0, year: 0, time: "" },
endDTG : {day: 0, month: 0, year: 0, time: "" },
mode: ""
},
...
],
blah...,
blah...,
blah...
}
I am struggling to find a more efficient way to sort the events objects based on the startDTG key (Date-Time Group). Currently I use the following, but I feel there has to be a better way to do it!
SortEvents: function() {
this.Events.events.sort(function(a, b){return a.startDTG.time - b.startDTG.time});
this.Events.events.sort(function(a, b){return a.startDTG.day - b.startDTG.day});
this.Events.events.sort(function(a, b){return a.startDTG.month - b.startDTG.month});
this.Events.events.sort(function(a, b){return a.startDTG.year - b.startDTG.year});
},
Edit 1: The desire is to be sorted by Year > Month > Day > Time
I am at a critical point in which I am to abandon this custom DTG in the name of efficiency it is needed. I can post the entire code if requested, but might not make total sense as it is JS written to work within a Proprietary Control system called "Medialon"
Edit 2: Added a quick-made JSON code dump below to assist with readability of structure. Ignore the fact they are all "strings" it is how Medialon stringifies for persistence
{
"events": [
{
"startDTG": {
"day": "8",
"month": "2",
"year": "2019",
"time": "06:35",
"dayName": "5"
},
"endDTG": {
"day": "9",
"month": "2",
"year": "2019",
"time": "08:35",
"dayName": "6"
},
"mode": "1"
},
{
"startDTG": {
"day": "27",
"month": "2",
"year": "2019",
"time": "17:35",
"dayName": "3"
},
"endDTG": {
"day": "28",
"month": "2",
"year": "2019",
"time": "06:35",
"dayName": "4"
},
"mode": "1"
},
{
"startDTG": {
"day": "1",
"month": "2",
"year": "2019",
"time": "14:35",
"dayName": "5"
},
"endDTG": {
"day": "2",
"month": "2",
"year": "2019",
"time": "12:35",
"dayName": "6"
},
"mode": "1"
}
],
I'm still not quite sure of your data structure, but something like this should be close:
const events = [
{name: 'a', startDTG: {year: 2019, month: 1, day: 4, time: '14:21:46'}, endDTG: ''},
{name: 'b', startDTG: {year: 2018, month: 10, day: 7, time: '12:13:59'}, endDTG: ''},
{name: 'c', startDTG: {year: 2019, month: 1, day: 4, time: '09:23:51'}, endDTG: ''},
{name: 'd', startDTG: {year: 2019, month: 1, day: 2, time: '15:02:36'}, endDTG: ''},
{name: 'e', startDTG: {year: 2017, month: 9, day: 17, time: '03:25:29'}, endDTG: ''},
{name: 'f', startDTG: {year: 2017, month: 9, day: 17, time: '03:25:28'}, endDTG: ''},
{name: 'g', startDTG: {year: 2018, month: 4, day: 14, time: '11:07:42'}, endDTG: ''},
]
events.sort((
{startDTG: {year: y1, month: m1, day: d1, time: t1}},
{startDTG: {year: y2, month: m2, day: d2, time: t2}}
) =>
// y1 - y2 || m1 - m2 || d1 - d2 || (t1 < t2 ? -1 : t1 > t2 ? 1 : 0)
y1 - y2 || m1 - m2 || d1 - d2 || t1.localeCompare(t2)
)
console.log(events)
Another solution could be mapping your data to a Date() and then comparing the milliseconds returned with getTime().
let data = {
"events": [
{
"startDTG": {"day": "8", "month": "2", "year": "2019", "time": "6:35", "dayName": "5"},
"endDTG": {"day": "9", "month": "2", "year": "2019", "time": "6:35", "dayName": "6"},
"mode": "1"
},
{
"startDTG": {"day": "27", "month": "2", "year": "2019", "time": "6:35", "dayName": "3"},
"endDTG": {"day": "28", "month": "2", "year": "2019", "time": "6:35", "dayName": "4"},
"mode": "1"
},
{
"startDTG": {"day": "1", "month": "2", "year": "2019", "time": "6:35", "dayName": "5"},
"endDTG": {"day": "2", "month": "2", "year": "2019", "time": "6:35", "dayName": "6"},
"mode": "1"
}
]
};
const startDTGToStr = o => `${o.year}-${o.month}-${o.day} ${o.time}`
data.events.sort((a, b) =>
{
a = new Date(startDTGToStr(a.startDTG));
b = new Date(startDTGToStr(b.startDTG));
return a.getTime() - b.getTime();
});
console.log(data.events);

How do you sort an array by two fields (date included) [duplicate]

This question already has answers here:
Sorting an array of objects by property values
(35 answers)
Closed 4 years ago.
I have an array of objects that I want to sort first by date and next by its numeric value.
let arr = [
{date: 2018-06-19 12:05:43.232Z, value: 3},
{date: 2018-06-20 12:05:43.232Z, value: 4},
{date: 2018-06-18 12:05:43.232Z, value: 2},
{date: 2018-06-20 12:05:43.232Z, value: 4},
{date: 2018-06-19 12:05:43.232Z, value: 5},
{date: 2018-06-18 12:05:43.232Z, value: 5},
{date: 2018-06-20 12:05:43.232Z, value: 5},
{date: 2018-06-19 12:05:43.232Z, value: 4},
]
I want to sort each index by the date and the value so the result would be :
let arr = [
{date: 2018-06-18 12:05:43.232Z, value: 2},
{date: 2018-06-18 12:05:43.232Z, value: 5},
{date: 2018-06-19 12:05:43.232Z, value: 3},
{date: 2018-06-19 12:05:43.232Z, value: 4},
{date: 2018-06-19 12:05:43.232Z, value: 5},
{date: 2018-06-20 12:05:43.232Z, value: 3},
{date: 2018-06-20 12:05:43.232Z, value: 4},
{date: 2018-06-20 12:05:43.232Z, value: 5},
]
How can it be done?
You can use Array.sort with ||
const res = arr.sort((a, b) => Date.parse(a.date) - Date.parse(b.date) || a.value - b.value);
console.log(res);
<script>
let arr = [{
date: '2018-06-19 12:05:43.232Z',
value: 3
},
{
date: '2018-06-20 12:05:43.232Z',
value: 4
},
{
date: '2018-06-18 12:05:43.232Z',
value: 2
},
{
date: '2018-06-20 12:05:43.232Z',
value: 4
},
{
date: '2018-06-19 12:05:43.232Z',
value: 5
},
{
date: '2018-06-18 12:05:43.232Z',
value: 5
},
{
date: '2018-06-20 12:05:43.232Z',
value: 5
},
{
date: '2018-06-19 12:05:43.232Z',
value: 4
},
]
</script>
Yo can do it using lodash library. If you check the docs of orderBy function you'll see that, it is exactly what you need.
let arr = [
{"date": "2018-06-19 12:05:43.232Z", "value": 3},
{"date": "2018-06-20 12:05:43.232Z", "value": 4},
{"date": "2018-06-18 12:05:43.232Z", "value": 2},
{"date": "2018-06-20 12:05:43.232Z", "value": 4},
{"date": "2018-06-19 12:05:43.232Z", "value": 5},
{"date": "2018-06-18 12:05:43.232Z", "value": 5},
{"date": "2018-06-20 12:05:43.232Z", "value": 5},
{"date": "2018-06-19 12:05:43.232Z", "value": 4}
]
Applying _orderBy on your array as follows:
_.orderBy(arr, ['date', 'value'], ['asc', 'asc']);
will get you the result that you want.
Check the example below:
let arr = [
{"date": "2018-06-19 12:05:43.232Z", "value": 3},
{"date": "2018-06-20 12:05:43.232Z", "value": 4},
{"date": "2018-06-18 12:05:43.232Z", "value": 2},
{"date": "2018-06-20 12:05:43.232Z", "value": 4},
{"date": "2018-06-19 12:05:43.232Z", "value": 5},
{"date": "2018-06-18 12:05:43.232Z", "value": 5},
{"date": "2018-06-20 12:05:43.232Z", "value": 5},
{"date": "2018-06-19 12:05:43.232Z", "value": 4}
]
console.log(_.orderBy(arr, ['date', 'value'], ['asc', 'asc']))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>

Categories

Resources