Sort Array Object in Array Object javascript - javascript

how to sort highest value in this data using javascript?
data = [{a: [{num:31}, {num:10}]},{a: [{num:4}, {num:9}]},{a: [{num:5}, {num:9}]}]
Expected
data = [{a: [{num:31}]},{a: [{num:9}]},{a: [{num:9}]}]
I try like this but never happen :)
const data_sort = data.sort((a, b) => {
let abc
if (a.a.length > 0) {
abc = a.a.sort((x, y) => x.a - x.a);
}
return a - b
})

let data = [{a: [{num:31}, {num:10}]},{a: [{num:4}, {num:9}]},{a: [{num:5}, {num:9}]}]
data = data.map(item => ({a:[item.a.sort((a, b) => b.num-a.num)[0]]})).sort((a, b) => b.a[0].num-a.a[0].num)
console.log(data)

Assuming this is the correct syntax, all you need is to map every item in the array to it's largest item, then sort that.
var data = [{
a: [{num:31}, {num:10}]
}, {
a: [{num:4}, {num:9}]
}, {
a: [{num:6}, {num:11}]
}, {
a: [{num:5}, {num:9}]
}];
var result = data.map(function(item) {
return {
a: [item.a.sort(function(a,b) {
return a.num - b.num
}).reverse()[0]]
};
}).sort(function(a, b) {
return a.a[0].num - b.a[0].num
}).reverse();
console.log(result)
.as-console-wrapper {
max-height: 100% !important;
}

Map and Reduce
const data = [{ a: [{ num: 31 }, { num: 10 }] }, { a: [{ num: 4 }, { num: 9 }] }, { a: [{ num: 5 }, { num: 9 }] }];
data.map((value) => {
value.a = [
{
num: value.a.reduce((accumulatedValue, currentValue) => {
return Math.max(accumulatedValue.num, currentValue.num);
}),
},
];
return value;
});
console.log(data)

Related

Issue arranging values in ascending order [duplicate]

This question already has answers here:
Sort Array Elements (string with numbers), natural sort
(8 answers)
Closed 11 months ago.
I am trying to arrange given values in ascending orders
const value = [
{ val: "11-1" },
{ val: "12-1b" },
{ val: "12-1a" },
{ val: "12-700" },
{ val: "12-7" },
{ val: "12-8" },
];
I am using code below to sort this in ascending order:
value.sort((a,b)=>(a.val >b.val)? 1:((b.val>a.val)?-1:0));
The result of this sort is in the order 11-1,12-1a, 12-1b, 12-7, 12-700, 12-8. However, I want the order to be 11-1,12-1a, 12-1b, 12-7, 12-8, 12-700.
How can I achieve that?
If you're only interested of sorting by the value after the hyphen you can achieve it with this code:
const value = [
{val:'12-1'},
{val:'12-700'},
{val:'12-7'},
{val:'12-8'},
];
const sorted = value.sort((a,b) => {
const anum = parseInt(a.val.split('-')[1]);
const bnum = parseInt(b.val.split('-')[1]);
return anum - bnum;
});
console.log(sorted);
updated the answer as your question update here's the solution for this:
const value = [{ val: '11-1' }, { val: '12-1b' }, { val: '12-1a' }, { val: '12-700' }, { val: '12-7' }, { val: '12-8' }];
const sortAlphaNum = (a, b) => a.val.localeCompare(b.val, 'en', { numeric: true });
console.log(value.sort(sortAlphaNum));
You can check the length first and then do the sorting as follow:
const value = [
{ val: "12-1" },
{ val: "12-700" },
{ val: "12-7" },
{ val: "12-8" },
];
const result = value.sort(
(a, b)=> {
if (a.val.length > b.val.length) {
return 1;
}
if (a.val.length < b.val.length) {
return -1;
}
return (a.val >b.val) ? 1 : ((b.val > a.val) ? -1 : 0)
}
);
console.log(result);
little change's to #Christian answer it will sort before and after - value
const value = [{ val: '12-1' }, { val: '12-700' }, { val: '11-7' }, { val: '12-8' }];
const sorted = value.sort((a, b) => {
const anum = parseInt(a.val.replace('-', '.'));
const bnum = parseInt(b.val.replace('-', '.'));
return anum - bnum;
});
console.log(sorted);
If you want to check for different values both before and after the hyphen and include checking for letters, the solution at the end will solve this.
Here's what I did:
Created a regex to split the characters by type:
var regexValueSplit = /(\d+)([a-z]+)?-(\d+)([a-z]+)?/gi;
Created a comparison function to take numbers and letters into account:
function compareTypes(alpha, bravo) {
if (!isNaN(alpha) && !isNaN(bravo)) {
return parseInt(alpha) - parseInt(bravo);
}
return alpha > bravo;
}
Split the values based on regexValueSplit:
value.sort((a, b) => {
let valuesA = a.val.split(regexValueSplit);
let valuesB = b.val.split(regexValueSplit);
This produces results as follows (example string "12-1a"):
[
"",
"12",
null,
"1",
"a",
""
]
Then, since all the split arrays should have the same length, compare each value in a for loop:
for (let i = 0; i < valuesA.length; i++) {
if (valuesA[i] !== valuesB[i]) {
return compareTypes(valuesA[i], valuesB[i]);
}
}
// Return 0 if all values are equal
return 0;
const value = [{
val: "11-1"
},
{
val: "12-1b"
},
{
val: "12-1a"
},
{
val: "12-700"
},
{
val: "12-7"
},
{
val: "12-8"
},
];
var regexValueSplit = /(\d+)([a-z]+)?-(\d+)([a-z]+)?/gi;
function compareTypes(alpha, bravo) {
if (!isNaN(alpha) && !isNaN(bravo)) {
return parseInt(alpha) - parseInt(bravo);
}
return alpha > bravo;
}
value.sort((a, b) => {
let valuesA = a.val.split(regexValueSplit);
let valuesB = b.val.split(regexValueSplit);
for (let i = 0; i < valuesA.length; i++) {
if (valuesA[i] !== valuesB[i]) {
return compareTypes(valuesA[i], valuesB[i]);
}
}
return 0;
});
console.log(JSON.stringify(value, null, 2));
Since you are sorting on string values, try using String.localeCompare for the sorting.
Try sorting on both numeric components of the string.
const arr = [
{val:'12-1'},
{val:'11-900'},
{val:'12-700'},
{val:'12-7'},
{val:'11-1'},
{val:'12-8'},
{val:'11-90'},
];
const sorter = (a, b) => {
const [a1, a2, b1, b2] = (a.val.split(`-`)
.concat(b.val.split(`-`))).map(Number);
return a1 - b1 || a2 - b2; };
console.log(`Unsorted values:\n${
JSON.stringify(arr.map(v => v.val))}`);
console.log(`Sorted values:\n${
JSON.stringify(arr.sort(sorter).map(v => v.val))}`);

How to add elements to specific index conditionaly?

I have an array of objects and I want to add an element to specific index when a certain attribute changes compared to the previous one.
We have:
const arr = [
{ num: 1 },
{ num: 1 },
{ num: 1 },
{ num: 3 },
{ num: 3 },
{ num: 4 },
{ num: 5 },
];
I want it to become
const arr = [
{ separator:true }
{ num: 1 },
{ num: 1 },
{ num: 1 },
{ separator:true }
{ num: 3 },
{ num: 3 },
{ separator:true }
{ num: 4 },
{ separator:true }
{ num: 5 },
];
I did this:
const getIndexes = (myArr) => {
let indexes = [];
let previousValue = null;
myArr.forEach((el, idx) => {
if (el.num !== previousValue) {
indexes.push(idx);
previousValue = el.num;
}
});
return indexes;
};
const insertSeparator = (arr) => {
let result = arr;
getIndexes(arr).forEach((position) => result.splice(position, 0, { separator: true }));
return result
};
and it returns:
[
{ separator: true },
{ num: 1 },
{ num: 1 },
{ separator: true },
{ num: 1 },
{ separator: true },
{ separator: true },
{ num: 3 },
{ num: 3 },
{ num: 4 },
{ num: 5 }
]
Maybe because of the "new" size of the array, because it is getting bigger and changes its dimension.
What do you think is the best way to solve this?
Run it through .flatMap()
const result = arr.flatMap((obj, idx, arr) => {...
.flatMap() is .map() and .flat() combined, so it transforms the contents of a copy of the given array and removes the brackets []. Next, we return the first object with a separator:
if (idx == 0) {
// returns are wrapped in brackets because they'll be removed before being returned
return [{separator: true}, obj];
}
The next step is to compare the current value with the previous value:
obj.num == arr[idx - 1].num ? // current value vs previous value
[arr[idx - 1]] : // if they are the same value return previous value
[{separator: true}, obj]; /* if they are not the same then return that separator
and current */
const arr = [
{ num: 1 },
{ num: 1 },
{ num: 1 },
{ num: 3 },
{ num: 3 },
{ num: 4 },
{ num: 5 },
];
const result = arr.flatMap((obj, idx, arr) => {
if (idx == 0) {
return [{
separator: true
}, obj];
}
return obj.num == arr[idx - 1].num ? [arr[idx - 1]] : [{
separator: true
}, obj];
});
console.log(JSON.stringify(result, null, 2));
I propose this solution which would consume only one iteration with a reduce :
const arr = [{
num: 1
},
{
num: 1
},
{
num: 1
},
{
num: 3
},
{
num: 3
},
{
num: 4
},
{
num: 5
},
];
let prev_value = arr[0];
const result = arr.reduce((acc, val) => {
const insert = (val.num !== prev_value.num) ? [{
separator: true
}, val] : [val];
prev_value = val;
return acc.concat(insert)
}, [{
separator: true
}, ])
console.log(result)
There must be other ways to do it too. But with a simple modification to your code it can be done. You just need to keep track of the offset with a new variable, incrementing it in the loop:
const arr = [
{ num: 1 },
{ num: 1 },
{ num: 1 },
{ num: 3 },
{ num: 3 },
{ num: 4 },
{ num: 5 },
];
const getIndexes = (myArr) => {
let indexes = [];
let previousValue = null;
myArr.forEach((el, idx) => {
if (el.num !== previousValue) {
indexes.push(idx);
previousValue = el.num;
}
});
return indexes;
};
const insertSeparator = (arr) => {
let result = [...arr];
let offset = -1;
getIndexes(arr).forEach((position) => {
offset++;
return result.splice(position+offset, 0, { separator: true });
});
return result
};
console.log(insertSeparator(arr));
Note: If you want to start with 0 you can do the increment in the .splice() itself : result.splice(position+(offset++),
const positions = [];
//arr.sort((a, b) => a.num - b.num); You can uncomment this line to ensure that the array will always sorted based on num property
arr.forEach((item, index) => {
if (index < arr.length - 1 && item.num != arr[index + 1].num) {
positions.push(index + 1);
}
});
let counter = 0;
positions.forEach((pos) => {
arr.splice(pos + counter++, 0, { separator: true });
});
console.log(arr);
You want to:
Do something which each item in a list
Want to return something other than a list of the same size.
Then I would suggest the good all-round Array.prototype.reduce() function.
const separator = {separator: true};
arr.reduce((result, item) => {
if (result.at(-1)?.num === item.num) {
return [...result, separator, item];
}
return [...result, item]
}, [])
This is (according to me) easier, cleaner and safer since it doesn't mutate variables.
Note
Array.prototype.at() is at the time of writing a new function. If you are using an ancient browser that doesn't support it you can use arr[arr.length -1] to get the last item instead.

NodeJS Json sort by child array

I want to sort my object by it´s child array.
let partners = [
{
Name: "A",
Sectors: [
{ Code: "AA1", Total: 10 },
{ Code: "AA2", Total: 70 }
]
},
{
Name: "B",
Sectors: [
{ Code: "AA1", Total: 20 },
{ Code: "AA2", Total: 60 }
]
},
{
Name: "C",
Sectors: [
{ Code: "AA1", Total: 40 },
{ Code: "AA2", Total: 50 }
]
}
];
Lets say I want to sort by the prop Sectors where Code = AA1 DESC. The result (prop Name) would be:
C, B, A
Or if I need Code = AA2 DESC, would be A, B, C
I had some dificults with a simple sort like this:
let sorted = partners.sort((a, b) => a.Sectors.Total - b.Sectors.Total);
sorted.forEach((sortedData) => console.log(sortedData));
Here is a SandBox
Here is the correct answer with a change to acept empty.
let partners = [
{"Name":"E","Sectors": null},
{"Name":"A","Sectors":[{"Code":"AA1","Total":10},{"Code":"AA2","Total":70}]},
{"Name":"B","Sectors":[{"Code":"AA1","Total":20},{"Code":"AA2","Total":60}]},
{"Name":"C","Sectors":[{"Code":"AA1","Total":40},{"Code":"AA2","Total":50}]},
{"Name":"D","Sectors": null},
{"Name":"P","Sectors": []}
]
partners.sort((a, b) => {
const get = (arr, key, val) => {
return arr.find(o => o[key] === val) || {};
}
let sA = {}, sB = {}
try {
sA = get(a.Sectors, 'Code', 'AA1')
sB = get(b.Sectors, 'Code', 'AA1')
} catch (e) {}
return !!sB.Total - !!sA.Total || sB.Total - sA.Total
})
console.log(partners)
You could to this with sort and find methods. Inside the sort you need to find element from the Sectors array with specific Code and then compare the Total values.
let partners = [{"Name":"A","Sectors":[{"Code":"AA1","Total":10},{"Code":"AA2","Total":70}]},{"Name":"B","Sectors":[{"Code":"AA1","Total":20},{"Code":"AA2","Total":60}]},{"Name":"C","Sectors":[{"Code":"AA1","Total":40},{"Code":"AA2","Total":50}]}]
partners.sort((a, b) => {
const sA = a.Sectors.find(({ Code }) => Code === 'AA1');
const sB = b.Sectors.find(({ Code }) => Code === 'AA1');
return sB.Total - sA.Total
})
console.log(partners)
You could take a dynamic approach and find the wanted value for sorting.
const
sortBy = (fn, order) => (a, b) => (order === 'ASC' || -1) * (fn(a) - fn(b)),
getValue = code => o => o.Sectors.find(({ Code }) => Code === code)?.Total || 0,
partners = [{ Name: "A", Sectors: [{ Code: "AA1", Total: 10 }, { Code: "AA2", Total: 70 }] }, { Name: "B", Sectors: [{ Code: "AA1", Total: 20 }, { Code: "AA2", Total: 60 }] }, { Name: "C", Sectors: [{ Code: "AA1", Total: 40 }, { Code: "AA2", Total: 50 }] }];
console.log(partners.sort(sortBy(getValue('AA1'), 'DESC')));
console.log(partners.sort(sortBy(getValue('AA2'), 'DESC')));
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to flatten array in JS?

I have a data that is like following:
const data = [{
ratings: [ { rating: 5 } ],
counts: [ { count: 100 } ],
}];
And I want to flatten it in a sense that I want to get rid of arrays and have only objects, and end result to be:
const data = {
ratings: { rating: 5 },
counts: { count: 100 },
};
I tried to do something like this, but it is wrong and I believe I'm kind of over complicating it.
const flatten = data => {
return data.reduce((r, { ...children }) => {
Object.assign(children, r);
if (children) Object.assign(flatten(...Object.values(children)), r);
return r;
}, {})
}
Any ideas?
You could create recursive function with reduce method to turn all arrays to objects assuming you have just objects in those arrays.
const data = [{ratings: [ { rating: 5 } ],counts: [ { count: 100 } ]}];
function flatten(arr) {
return arr.reduce((r, e) => {
const obj = Object.assign({}, e);
for (let p in obj) {
if (Array.isArray(obj[p])) {
obj[p] = flatten(obj[p])
}
}
return Object.assign(r, obj)
}, {})
}
console.log(flatten(data))
If by any chance the data is result from JSON.parse :
var json = JSON.stringify( [{ratings:[{rating: 5}], counts:[{count: 100}]}] )
var result = JSON.parse(json, (k, v) => v[0] || v)
console.log( result )
Please check:
var data = [{ratings: [ { rating: 5 } ], counts: [ { count: 100 } ]}];
var flatten = function(data) {
if (Array.isArray(data)) {
data = data[0];
for (var key in data) data[key] = flatten(data[key]);
}
return data;
}
console.log(flatten(data));
Please check # CodePen
https://codepen.io/animatedcreativity/pen/842e17d2b9f83bc415513f937fc29be8

How to update an array of objects according another array of objects

I have two arrays:
initial=[{code:"1",size: 0},{code:"2",size: 0},{code:"3",size: 0},{code:"4",size: 0}];
update=[{code:"1",size: 100},{code:"2",size: 100},{code:"2",size: 120}];
I need to get new array like this:
I want to group by code field and do the sum of size after grouping.
new=[{code:"1",size: 100},{code:"2",size: 220},{code:"3",size: 0},{code:"4",size: 0}];
You could use a hash table for the objects with same code.
var initial = [{ code: "1", size: 0 }, { code: "2", size: 0 }, { code: "3", size: 0 }, { code: "4", size: 0 }],
update = [{ code: "1", size: 100 }, { code: "2", size: 100 }, { code: "2", size: 120 }],
hash = Object.create(null),
result = initial.map(function (o) {
return hash[o.code] = { code: o.code, size: 0 };
});
update.forEach(function (o) {
hash[o.code].size += o.size;
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Check this
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
var o1 = { a: 1, b: 1, c: 1 };
var o2 = { b: 2, c: 2 };
var o3 = { c: 3 };
var obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
You can use .map(), .filter() and .reduce() methods of Array.prototype
let initial = [{code:"1",size: 0},{code:"2",size: 0},{code:"3",size: 0},{code:"4",size: 0}];
let update = [{code:"1",size: 100},{code:"2",size: 100},{code:"2",size: 120}];
let res = initial.map(({code, size}, index) =>
({code, size: size
// get elements having same `"code"` property value from `update` array
// add the result
+ update.filter(({code:c}) => code === c)
.reduce((n, {size:s}) => n+= s, 0)})
);
console.log(res);
Here is a version using an ES6 Map in a functional programming style:
function addObjects(acc, add) {
return Array.from(
add.reduce((mp, {code, size}) => mp.set(code, mp.get(code) + size),
new Map(acc.map(({code, size}) => [code, size]))),
([code, size]) => ({code, size})
);
}
const initial = [{ code: "1", size: 0 }, { code: "2", size: 0 }, { code: "3", size: 0 }, { code: "4", size: 0 }],
update = [{ code: "1", size: 100 }, { code: "2", size: 100 }, { code: "2", size: 120 }];
const res = addObjects(initial, update);
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use array#concat to join your array and then use array#reduce to sum up the sizes inside an object and then get the values from that object.
const initial=[{code:"1",size: 0},{code:"2",size: 0},{code:"3",size: 0},{code:"4",size: 0}],
update=[{code:"1",size: 100},{code:"2",size: 100},{code:"2",size: 120}];
let result = initial
.concat(update)
.reduce((res, {code, size}) => {
res[code] = {code, size : (res[code]|| {size:0}).size + size};
return res;
},Object.create(null));
let output = Object.values(result);
console.log(output);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories