Group array based on many values - javascript

I want to group the following array of products:-
const array = [{date: "10 feb", from: "2", to: "10", name:"chair"},
{date: "10 feb", from: "10", to: "22", name: "chair"},
{date: "10 feb", from: "22", to: "31", name: "chair"},
{date: "10 feb", from: "35", to: "40", name: "chair"},
{date: "12 feb", from: "2", to: "10", name: "chair"},
{date: "12 feb", from: "10", to: "20", name: "toy"}]
Such as if the date and name of consecutive product are similar and also "from" of 1st product is same as "to" of the next product then they should be grouped by taking "from" of 1st product and "to" of next product.
Result array = [{date: "10 feb", from: "2", to: "31", name:"chair"},
{date: "10 feb", from: "35", to: "40", name: "chair"},
{date: "12 feb", from: "2", to: "10", name: "chair"},
{date: "12 feb", from: "10", to: "20", name: "toy"}]
Preferable solution using Foreach loop rather than for loop.

Make use of reduce function to achieve the desired result.
var array = [{date: "10 feb", from: "2", to: "10", name:"chair"}, {date: "10 feb", from: "10", to: "22", name: "chair"}, {date: "10 feb", from: "22", to: "31", name: "chair"}, {date: "10 feb", from: "35", to: "40", name: "chair"}, {date: "12 feb", from: "2", to: "10", name: "chair"}, {date: "12 feb", from: "10", to: "20", name: "toy"}];
var result = array.reduce((acc, elem)=>{
index = acc.findIndex(val=>val.to == elem.from && val.date == elem.date && val.name==elem.name);
index == -1 ? acc.push(elem) : acc[index].to = elem.to
return acc;
},[]);
console.log(result);
I hope this helps. Thanks!

Related

Pivot/Transform json data columns to create new rows in javascript

I have a json file in the following format:
const data = [
{category: "A", country: "UK", name: "United Kingdom", country_id: "01", 2015: 5, 2016: 56, 2017: 10},
{category: "B", country: "UK", name: "United Kingdom", country_id: "01", 2015: 4, 2016: 10, 2017: 10},
{category: "C", country: "UK", name: "United Kingdom", country_id: "01", 2015: 10, 2016: 7, 2017: 45},
{category: "A", country: "PO", name: "Poland", country_id: "02", 2015: 9, 2016: 14, 2017: 10},
{category: "B", country: "PO", name: "Poland", country_id: "02", 2015: 10, 2016: 40, 2017: 0},
{category: "C", country: "PO", name: "Poland", country_id: "02", 2015: 60, 2016: 30, 2017: 74},
{category: "A", country: "CZ", name: "Czech Republic", country_id: "03", 2015: 30, 2016: 20, 2017: 10},
{category: "B", country: "CZ", name: "Czech Republic", country_id: "03", 2015: 15, 2016: 28, 2017: 1},
{category: "C", country: "CZ", name: "Czech Republic", country_id: "03", 2015: 16, 2016: 10, 2017: 2}
]
and I want to pivot the data to get the following format:
move 2015, 2016 and 2017 into a property named year
create a, b and c properties from category property values that will contain the different values.
have a line/object per country and year and any other ordinal categories I would like to keep.
const data = [
{country: "UK", name: "United Kingdom", country_id: "01", year: "2015", "a": 5 , "b": 4, "c": 10},
{country: "UK", name: "United Kingdom", country_id: "01", year: "2016", "a": 56 , "b": 10, "c": 7},
{country: "UK", name: "United Kingdom", country_id: "01", year: "2017", "a": 10 , "b": 10, "c": 45},
{country: "PO", name: "Poland", country_id: "02", year: "2015", "a": 9 , "b": 10, "c": 80},
{country: "PO", name: "Poland", country_id: "02", year: "2016", "a": 14 , "b": 40, "c": 30},
{country: "PO", name: "Poland", country_id: "02", year: "2017", "a": 10 , "b": 0, "c": 74},
{country: "CZ", name: "Czech Republic", country_id: "03", year: "2015", "a": 30 , "b": 15, "c": 16},
{country: "CZ", name: "Czech Republic", country_id: "03", year: "2016", "a": 20 , "b": 28, "c": 1},
{country: "CZ", name: "Czech Republic", country_id: "03", year: "2017", "a": 10 , "b": 1, "c": 2}
I tried writing a for loop inside a map method but I am unable to create a, b and c properties.
The rotation is done on the 3 lines commented 'Rotation'
To be able to do this we need to be able to access multiple rows of the original dataset. The strategy here builds us up to be able to do that.
Step 1. Get lists of the unique country_ids, years and categories
There are several ways to do this, and I have shown the easiest to understand method, which is to convert to a Set (which automatically removes duplicates) and then back to an Array for convenience of use.
Step 2. Move from a simple array, into an object
Instead of the rows being simply in sequence 0...8, we now have them in a 3x3 grid, addressible by country and category.
Step 3. Construct the desired output
Now within each country, we can extract all the data for a chosen year, by "plucking" the values for this year from the three different categories in the original data.
const data = [
{category: "A", country: "UK", name: "United Kingdom", country_id: "01", 2015: 5, 2016: 56, 2017: 10},
{category: "B", country: "UK", name: "United Kingdom", country_id: "01", 2015: 4, 2016: 10, 2017: 10},
{category: "C", country: "UK", name: "United Kingdom", country_id: "01", 2015: 10, 2016: 7, 2017: 45},
{category: "A", country: "PO", name: "Poland", country_id: "02", 2015: 9, 2016: 14, 2017: 10},
{category: "B", country: "PO", name: "Poland", country_id: "02", 2015: 10, 2016: 40, 2017: 0},
{category: "C", country: "PO", name: "Poland", country_id: "02", 2015: 60, 2016: 30, 2017: 74},
{category: "A", country: "CZ", name: "Czech Republic", country_id: "03", 2015: 30, 2016: 20, 2017: 10},
{category: "B", country: "CZ", name: "Czech Republic", country_id: "03", 2015: 15, 2016: 28, 2017: 1},
{category: "C", country: "CZ", name: "Czech Republic", country_id: "03", 2015: 16, 2016: 10, 2017: 2}
]
// Step 1. Extract the unique country_id, category Ids and years
const country_ids = Array(...new Set(data.map((x) => x.country_id)));
const categories = Array(...new Set(data.map((x) => x.category)));
const years = ["2015","2016","2017"];
// Step 2. Convert the source data into an object so that you can conveniently read off particular rows, in terms of COUNTRY_ID and CATEGORY
const sourceRows = {};
data.forEach((row) => {
if (!sourceRows[row.country_id]) {
sourceRows[row.country_id] = {};
}
sourceRows[row.country_id][row.category] = row;
});
// You can visualise the output here with this, if you want:
// console.log(sourceRows)
// Step 3. Create destination array, and poke a row into it for each country & year.
const destination = [];
country_ids.forEach((country_id) => {
years.forEach((year) => {
const sourceRow = sourceRows[country_id][categories[0]];
const destRow = {
country_id: country_id,
name: sourceRow.name,
country: sourceRow.country,
year: year,
a: sourceRows[country_id]["A"][year], // Rotation
b: sourceRows[country_id]["B"][year], // Rotation
c: sourceRows[country_id]["C"][year] // Rotation
};
destination.push(destRow);
});
});
console.log(destination);
Not the best solution, but this works. At the and, I made a workaround for the duplicates. You could use ...rest parameter to initialize years array if there will be new data for other years.
let newData = [];
let countries = data.map(({country})=> country)
let categories = data.map(({category})=> category)
let years = [2015,2016,2017];
countries.forEach(country => {
let countryData = data.filter(({country:c}) => c==country);
let yearData = {2015:{},2016:{},2017:{}};
years.forEach(year => {
categories.forEach(category => {
yearData[year][category] = countryData.find(({category:cat}) => cat==category)[year]
})
})
let {name,country_id}= data.find(({country:c}) => c == country);
Object.entries(yearData).forEach(([year,categories]) => {
newData.push({country,name,country_id,year, ...categories})
})
newData = newData.filter((data,i) => i%9<3)
console.log(newData)
})

Is it possible to sort an array of object by property of another object?

So i have an array of object that looks like this:
group: [
0: {id: "16", name: "P1", courseId: "6", mentorId: "1", chatUrl: false, students: {0: 1, 1: 10, 2: 11},…}
1: {id: "17", name: "C1", courseId: "7", mentorId: "3", chatUrl: false, students: {0: 15, 1: 16, 2: 18},…}
2: {id: "22", name: "P2", courseId: "6", mentorId: "1", chatUrl: false, students: {0: 12, 1: 13, 2: 9},…}
3: {id: "23", name: "C2", courseId: "7", mentorId: "3", chatUrl: false, students: {0: 17, 1: 19, 2: 20},…}
4: {id: "24", name: "DEV", courseId: "10", mentorId: "1", chatUrl: false,…}].
i'm trying to sort it by mentor name which is property of object in another array that looks like this:
mentor: [
0: {id: "0", firstName: "Daniel", about: false,…}
1: {id: "1", firstName: "Mark", aboutl:false,…}
2: {id: "3", firstName: "Eric", about: false,…}
3: {id: "6", firstName: "John", about: false,…} ]
The groups are related to mentors by property mentorId, so i have to compare property mentorId from group array with id from mentor array to get all mentors for groups and then sort the group by those mentors firstName. Is this even possible?
The output should display all the groups sorted alphabeticly by mentor firstname, something like this:
group: [
0: {id: "23", name: "C2", courseId: "7", mentorId: "3", chatUrl: false, students: {0: 17, 1: 19, 2: 20},…}
1: {id: "17", name: "C1", courseId: "7", mentorId: "3", chatUrl: false, students: {0: 15, 1: 16, 2: 18},…}
2: {id: "22", name: "P2", courseId: "6", mentorId: "1", chatUrl: false, students: {0: 12, 1: 13, 2: 9},…}
3: {id: "16", name: "P1", courseId: "6", mentorId: "1", chatUrl: false, students: {0: 1, 1: 10, 2: 11},…}
4: {id: "24", name: "DEV", courseId: "10", mentorId: "1", chatUrl: false,…}
]
You could first sort the mentors array by firstName property and then create an object where the key is the id and the value is index of that object in the sorted array. Then you can just use sort method on groups array and sort by the value of mentorId property in order object.
const groups = [{"id":"16","name":"P1","courseId":"6","mentorId":"1","chatUrl":false,"students":{"0":1,"1":10,"2":11}},{"id":"17","name":"C1","courseId":"7","mentorId":"3","chatUrl":false,"students":{"0":15,"1":16,"2":18}},{"id":"22","name":"P2","courseId":"6","mentorId":"1","chatUrl":false,"students":{"0":12,"1":13,"2":9}},{"id":"23","name":"C2","courseId":"7","mentorId":"3","chatUrl":false,"students":{"0":17,"1":19,"2":20}},{"id":"24","name":"DEV","courseId":"10","mentorId":"1","chatUrl":false}]
const mentors = [{"id":"0","firstName":"Daniel","about":false},{"id":"1","firstName":"Mark","aboutl":false},{"id":"3","firstName":"Eric","about":false},{"id":"6","firstName":"John","about":false}]
mentors.sort((a, b) => a.firstName.localeCompare(b.firstName))
const order = mentors.reduce((r, { id }, i) => (r[id] = i, r), {})
groups.sort((a, b) => order[a.mentorId] - order[b.mentorId])
console.log(groups)
var group= [
{id: "17", name: "C1", courseId: "7", mentorId: "3", chatUrl: false, students: {0: 15, 1: 16, 2: 18}},
{id: "16", name: "P1", courseId: "6", mentorId: "1", chatUrl: false, students: {0: 1, 1: 10, 2: 11}},
{id: "22", name: "P2", courseId: "6", mentorId: "1", chatUrl: false, students: {0: 12, 1: 13, 2: 9}},
{id: "23", name: "C2", courseId: "7", mentorId: "3", chatUrl: false, students: {0: 17, 1: 19, 2: 20}},
{id: "24", name: "DEV", courseId: "10", mentorId: "1", chatUrl: false}]
var mentor=[
{id: "0", firstName: "Daniel", about: false},
{id: "1", firstName: "Mark", aboutl:false},
{id: "3", firstName: "Eric", about: false},
{id: "6", firstName: "John", about: false}
]
group = group.sort( (a,b) => {
for(i=0;i<mentor.length;i++){
if (a.mentorId===mentor[i].id){a.mentorName=mentor[i].firstName}
if (b.mentorId===mentor[i].id){b.mentorName=mentor[i].firstName}
}
if(a.mentorName<b.mentorName){return -1}
else if(a.mentorName==b.mentorName){return 0}
else{return 1}
});
console.log(group);

Protractor: How do you retrieve data from multiple cells from multiple rows?

I am trying to get data and assert it from multiple cells from multiple rows.
I have a table with 10 rows and 13 cells in each row. I need to get values and assert only from cells 1, 2, 3 and 5.
This is the code I have:
let rows = element.all(by.tagName('tr'));
let data = rows.map((row) => {
let cells = row.all(by.tagName('td'));
return {
position: cells.get(1).getText(),
category: cells.get(2).getText(),
value: cells.get(3).getText(),
points: cells.get(5).getText()
}
})
expect(data).to.deep.equal([
{position: "1", category: "test1", value: "11", points: "3"},
{position: "2", category: "test2", value: "12", points: "5"},
{position: "3", category: "test3", value: "13", points: "3"},
{position: "4", category: "test4", value: "14", points: "5"},
{position: "5", category: "test5", value: "15", points: "3"},
{position: "6", category: "test6", value: "16", points: "5"},
{position: "7", category: "test7", value: "17", points: "3"},
{position: "8", category: "test8", value: "18", points: "5"},
{position: "9", category: "test9", value: "19", points: "3"},
{position: "10", category: "test10", value: "20", points: "5"}
]);
How can I fix this? Currently I get this error:
AssertionError: expected { Object (flow_, stack_, ...) } to deeply equal [ Array(10) ]
Why you dont use just expect([]).toEqual([])
If you have trouble with the object, try with stringify.
expect(JSON.stringify(data)).toEqual('[{"....."}]');
or
expect(JSON.stringify(data)).toMatch('[{"....."}]');

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