I have an array of objects:
[
{ id: 'ade', value: 8 },
{ id: 'rtl', value: 17 },
{ id: 'enu', value: 11 }
]
and want to convert that to:
[
{ id: 'enu', value: 11 },
{ id: 'rtl', value: 17 },
{ id: 'ade', value: 8 },
]
but I can't reverse it etc, because I have to set a statement that item { id: 'ade', value: 8 } should be last item and rest must be sorted by id.
I tried to reduce first array and concat with the second one
const firstArr = arr.reduce((acc, cv) => {
if (cv.id === 'ade') {
acc.push(cv)
}
return acc
}, []))
const second = arr.splice(firstArr, 1).sort()
firstArr.concat(second)
but it failed and seems over-engineering. I'd like to do it smarter. Any ideas?
Please about any advice!!
You can use the spread operator to make the code look shorter,
But it's the same logic:
const arr = //...your array of data
const parsedArr = [...arr.filter(o => o.id !== 'ade').sort((a, b) => a.id.localeCompare(b.id)), ...arr.filter(o => o.id === 'ade')];
so the first part is all the array elements without the specific one that you need to be in the end, and the second part is the last element.
Hope I helped :)
You can filter, sort and then concatinate array with own rules:
let orig = [
{ id: 'ade', value: 8 },
{ id: 'rtl', value: 17 },
{ id: 'enu', value: 11 }
]
sorted = orig.filter(e => e.id !== 'ade').sort((a, b) =>
a.id.localeCompare(b.id)
).concat(orig.find(e => e.id === 'ade'))
console.log(sorted)
[
{ id: 'ade', value: 8 },
{ id: 'rtl', value: 17 },
{ id: 'enu', value: 11 },
].sort((a,b) => a.id == 'ade' ? 1 : b.id == 'ade' ? -1 : a.id > b.id ? 1 : -1)
You can provide custom logic in the callback passed to sort() to handle elements whose id === 'ade' differently than other elements.
If either id equals "ade" return the difference of booleans, else compare the two ids using localeCompare.
const arr = [
{ id: 'ade', value: 8 },
{ id: 'rtl', value: 17 },
{ id: 'enu', value: 11 }
];
const sortedArr = [...arr].sort((a, b) => (
(a.id === 'ade' || b.id === 'ade')
? (a.id === 'ade') - (b.id === 'ade')
: a.id.localeCompare(b.id)));
console.log(sortedArr)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Example – for a = 'ade' and b = 'rtl' the difference of the booleans returned by comparing them to ade returns 1 which sorts b before a and so pushes ade downward.
(('ade'==='ade') - ('rtl' === 'ade')) -> (true - false) -> (1 - 0) -> 1
You can useArray.prototype.localeCompare for your case
let arr = [
{ id: 'ade', value: 8 },
{ id: 'rtl', value: 17 },
{ id: 'enu', value: 11 }
]
arr.sort((a,b) => `${a.value}`.localeCompare(`${b.value}`))
console.log(arr)
For this particular case you can use custom sort function:
const sortedArr = [...arr].sort((a, b) => {
return b.id.localeCompare(a.id)
})
But if in the arr will be more objects you need to refactor sort function
Related
I have an array of objects, objects that contain an order: number; property.
I want for each object in that array that has the order property higher than a specific value to have it decreased it by one.
Any simple way to achieve this?
myArray.forEach(x => x.order >= someValue)...
You can map the result, updating the value when >= someValue
const someValue = 3;
const result = [
{ order: 1 },
{ order: 2 },
{ order: 3 },
{ order: 4 }
].map(x => x.order >= someValue ? { ...x, order: x.order - 1 } : x);
console.log(result);
With array.reduce
const value = 2;
const testArray = [{ order: 1 }, { order: 1 }, { order: 3 }];
const result = testArray.reduce((acc, curr) => {
if (curr.order > value) {
return [...acc, { ...curr, order: curr.order - 1 }];
}
return [...acc, curr];
}, []);
console.log(result);
Several options to reach this:
Adding functionality to Array.prototype.
if(!Array.prototype.decreaseOrderIfBiggerThan) {
Array.prototype.decreaseOrderIfBiggerThan = function(value) {
if(!value || typeof value != 'number') return this;
return this.map((el) => ({
...el,
order: el?.order > value ? --el.order : el.order
}))
}
}
Simply mapping:
myArray = myArray.map((el) => ({
...el,
order: el?.order > value ? --el.order : el.order
}))
Mutating original array: it's basically #2 method but without assigning new array to original one and with forEach.
Hopes this is what you needed.
I have a large array of objects, similar to this:
[
{
id: "some_id",
timestamp: 12345
},
{
id: "some_other_id",
timestamp: 12347
},
{
id: "some_id",
timestamp: 12346
},
{
id: "some_other_id",
timestamp: 12348
},
...
]
I want to sort the array in a way, that there are "sections" of objects in the array, depending which id the object has. Inside each section, the objects should be sorted ascending depending on the timestamp. The sections themselves should also be sorted depending on the first timestamp of the section. So the array should look like this:
[
// section: some_id
{
id: "some_id",
timestamp: 12345
},
{
id: "some_id",
timestamp: 12348
},
// section: some_other_id, comes ofter some_id section because 12346 > 12345
{
id: "some_other_id",
timestamp: 12346
},
{
id: "some_other_id",
timestamp: 12347
},
...
]
It should also be possible to chose between ascending/descending in the function.
Right now i have this:
elements.sort((a, b) => {
if (a.id === b.id) {
if (sortAscending) {
return a.timestamp > b.timestamp ? -1 : 1;
} else {
return a.timestamp > b.timestamp ? 1 : -1;
}
} else {
return a.id.localeCompare(b.id);
}
})
This doesn't sort the sections correctly however. Any ideas?
This will require two passes through the array because you can't sort by sections until you know what the order of the sections should be and you don't know that until you've seen all the sections and thus know what the lowest or highest timestamp is for each section (depending upon whether you're doing ascending or descending sort).
So, probably what makes sense is to make a first pass that collects the extreme value for each section and stores it into a Map object that you can use as a section sort index. Then, you can run .sort(). If the sections are the same, you sort by timestamp. If the sections are not the same, you sort by the value in the section index.
function sortByIdAndTimestamp(data, sortAscending = true) {
// create a Map object where keys are id values and values are the extreme
// timestamp for that id
const extremeTimestamp = new Map();
for (let item of data) {
if (testExtreme(item.timestamp, extremeTimestamp.get(item.id), sortAscending)) {
extremeTimestamp.set(item.id, item.timestamp);
}
}
// now just do a dual key sort
data.sort((a, b) => {
let result;
if (a.id === b.id) {
// if id is the same, just sort by timestamp
result = b.timestamp - a.timestamp;
} else {
// if id is not the same, sort by the extreme timestamp of the id
result = extremeTimestamp.get(b.id) - extremeTimestamp.get(a.id);
}
if (sortAscending) {
result = -result;
}
return result;
});
return data;
}
function testExtreme(val, extremeSoFar, sortAscending) {
// determine if this id's timestamp is more extreme
// than what we already have for that section
return (extremeSoFar === undefined) || (sortAscending ?
val < extremeSoFar :
val > extremeSoFar);
}
const sampleData = [{
id: "some_id",
timestamp: 12345
},
{
id: "some_other_id",
timestamp: 123
},
{
id: "some_id",
timestamp: 12346
},
{
id: "some_other_id",
timestamp: 99999
},
{
id: "yet_another_id",
timestamp: 1
},
{
id: "yet_another_id",
timestamp: 90000
},
];
sortByIdAndTimestamp(sampleData, true);
console.log(sampleData);
Note: When you said you wanted to sort in either ascending or descending order, I assumed you meant that for both sections and for timestamps. So, an ascending sort would have the lowest timestamp section first and then the items within each section would be sorted by timestamp from low to high. And, a descending sort would have the highest timestamp section first and then items within each section would be sorted by timestamp from high to low.
You are not going to be able to do it in one sort. It is going to have to be multiple steps since you do not know what the minimum is.
One way is to combine, sort and than sort based on the lowest.
const data = [
{
id: "a",
timestamp: 4
},
{
id: "b",
timestamp: 3
},
{
id: "a",
timestamp: 2
},
{
id: "b",
timestamp: 1
},
];
const x = data.reduce((a,o) => {
a[o.id] = a[o.id] || [];
a[o.id].push(o);
return a;
}, {});
const v = Object.values(x);
v.forEach(x => x.sort((a,b) => a.timestamp > b.timestamp ? 1 : -1))
v.sort((a,b)=>a[0].timestamp > b[0].timestamp ? 1 : -1);
const sorted = v.flat();
console.log(sorted);
The other way is find the lowest and then sort with that.
const data = [{
id: "a",
timestamp: 4
},
{
id: "b",
timestamp: 3
},
{
id: "a",
timestamp: 2
},
{
id: "b",
timestamp: 1
},
];
const smallest = data.reduce((a ,o) => {
a[o.id] = Math.min(a[o.id] === undefined? Number.POSITIVE_INFINITY : a[o.id], o.timestamp);
return a;
}, {});
data.sort((a,b) => {
return a.id === b.id ?
(a.timestamp > b.timestamp ? 1 : -1)
: smallest[a.id] > smallest[b.id] ? 1 : -1;
})
console.log(data);
EDITED: Here's one way to do it if I understood correctly what you need
const data = [{
id: "some_id",
timestamp: 12348
},
{
id: "some_other_id",
timestamp: 12346
},
{
id: "some_id",
timestamp: 12345
},
{
id: "some_other_id",
timestamp: 12347
},
{
id: "X_other_id",
timestamp: 12343
},
{
id: "other_id",
timestamp: 12349
}
]
const groupById = (acc, item) => {
acc[item.id] ? acc[item.id].push(item) : acc[item.id] = [item];
return acc;
};
function sort(arr, bool) {
const groups = Object.values(arr.reduce(groupById, {}))
.map(group => group.sort((a, b) => bool ? a.timestamp - b.timestamp : b.timestamp - a.timestamp))
.sort((a, b) => bool ? a[0].timestamp - b[0].timestamp : b[0].timestamp - a[0].timestamp);
return groups.flat()
}
console.log(sort(data, ascending = true))
This question already has answers here:
Move an array element from one array position to another
(44 answers)
Closed 2 years ago.
I have an array of objects with this format
let arr = [ { name: "test1", id: 5}, { name: "test2", id: 6 } , { name: "test3", id: 8 } ]
Now I basically want to move the item with 6 to the front of the array by re-arranging so that result becomes
let result = [ { name: "test2", id: 6 } , { name: "test1", id: 5}, { name: "test3", id: 8 } ]
What I have tried
const found = arr.find((element) => {
return element.id === 6;
});
if (found) {
const [...arr, found] = arr;
return found;
} else {
return arr;
}
You can make use of Array.unshift and Array.splice.
let arr = [{name:"test1",id:5},{name:"test2",id:6},{name:"test3",id:8}]
const moveToFront = (data, matchingId) => {
//find the index of the element in the array
const index = data.findIndex(({id}) => id === matchingId);
if(index !== -1) {
//if the matching element is found,
const updatedData = [...data];
//then remove that element and use `unshift`
updatedData.unshift(...updatedData.splice(index, 1));
return updatedData;
}
//if the matching element is not found, then return the same array
return data;
}
console.log(moveToFront(arr, 6));
.as-console-wrapper {
max-height: 100% !important;
}
You could sort the array with the delta of the checks.
const
array = [{ name: "test1", id: 5 }, { name: "test2", id: 6 }, { name: "test3", id: 8 }];
array.sort((a, b) => (b.id === 6) - (a.id === 6));
console.log(array);
const array = [{ name: "test1", id: 5 }, { name: "test2", id: 6 }, { name: "test3", id: 8 }];
const sortedArray = array.sort((a, b) => (b.id === 6) - (a.id === 6)); console.log(sortedArray);
discusses an easy way to sort your JavaScript Array by the order of the index number for each item in the Array object. This can be helpful if you want to sort alphabetically and not need to create a new String Array with a function like String.sort().
A quick tip that can be useful to you if you want a quick solution to sorting an Array in JavaScript is below.Remember that it is always best practice to use the Array methods built into the language. They are created to work fast and efficient. However, if you really want to sort your array by index number and not have an array of strings, then this article will be for you.:
String→Number: When a function returns a Number value, then JavaScript interprets it as being equal to the Number value in the code...The function is used by passing two parameters, which should return true when they are equal and false when they are not equal.In this case, we are sort of reverse comparing them. We are checking the ID of the items inside the Array to see if they match, but we subtract one to check if they are less than. This is because when we call .sort(), JavaScript is sorting alphabetically and an ID with a value of 6 will be at the end of the list. So, a value of -1 will make it appear in the correct order.If you want to use this method for your Array, then please add a comment below!
You can use Array.unshift() to add element to the beginning of the array and Array.splice() to remove the array element.
let arr = [ { name: "test1", id: 5}, { name: "test2", id: 6 } , { name: "test3", id: 8 } ]
let result = [...arr];
const index = result.findIndex(e => e.id === 6)
result.unshift(result.splice(index, 1)[0])
console.log(result);
You can make use of filter and unshift
let arr = [{ name: "test1", id: 5 },{ name: "test2", id: 6 },{ name: "test3", id: 8 }];
let firstObject;
let result = arr.filter((value) => {
if (value.id != 6) return value;
firstObject = value;
});
result.unshift(firstObject);
console.log(result);
This question already has answers here:
Sort array containing objects based on another array [duplicate]
(2 answers)
Closed 2 years ago.
I have a following array of objects:
[
0: {
id: 1,
type: 'input'
},
1: {
id: 2,
type: 'boolean'
},
2: {
id: 3,
type: 'choice'
},
3: {
id: 1,
type: 'select'
},
]
I want to sort the array element based on the value of type. If type is 'choice' I want to place the element in the last place of array i.e. 4th place. If type is 'boolean', place it in third place. If type is 'select', place it in second place.
I know how to sort an array with numerical values.
arr.sort((a, b) => {
if (a.type < b.type) return -1
return a.type > b.type ? 1 : 0
})
I am having problem comparing property values and sorting it.
Please help me. Any help will be highly appreciated.
Regards,
Usually if need to sort an array based on some string property of object you need to create another which represents the order of sort. Then use indexOf and subtract the indexes
const order = ['input', 'select', 'boolean','choice'];
const arr = [
{
id: 1,
type: 'input'
},
{
id: 2,
type: 'boolean'
},
{
id: 3,
type: 'choice'
},
{
id: 1,
type: 'select'
},
]
arr.sort((a, b) => order.indexOf(a.type) - order.indexOf(b.type));
console.log(arr)
If you had
const arr = [
{
id: 1,
type: 'input',
val: 1
},
{
id: 2,
type: 'boolean',
val: 3
},
{
id: 3,
type: 'choice',
val: 4,
},
{
id: 1,
type: 'select',
val: 2
},
]
then it would be easy: just arr.sort((a, b) => a.val - b.val)
Since you don't have the property val, you can just set it on before:
const typeToValue = {
input: 1,
boolean: 3,
choice: 4,
select: 2
}
arr.forEach(el => {
el.val = typeToValue[el.type]
})
arr.sort((a, b) => a.val - b.val)
Maybe you don't want to dirty your elements, notice that el.val == typeToValue[el.type]
Meaning you can write
arr.sort((a, b)=>typeToValue[a.type] - typeToValue[b.type])
Finally should you have a sorted array ['input', 'select', 'boolean', 'choice'] you can trivially transform it to typeToValue object via Array.prototype.reduce
const orders = ['input', 'select', 'boolean', 'choice']
const typeToValue = orders.reduce((o, el, i) => (o[el] = i, o), {})
or if you don't like reduce with Object.fromEntries
const typeToValue = Object.fromEntries(orders.map((el, i) => [el, i]))
const arr = [{"id":1,"type":"input"},{"id":2,"type":"boolean"},{"id":3,"type":"choice"},{"id":1,"type":"select"}]
const orders = ['input', 'select', 'boolean', 'choice']
const typeToValue1 = orders.reduce((o, el, i) => (o[el] = i, o), {})
const typeToValue2 = Object.fromEntries(orders.map((el, i) => [el, i]))
// just slice to copy array because sort modify in place
console.log(arr.slice(0).sort((a, b)=>typeToValue1[a.type] - typeToValue1[b.type]))
console.log(arr.slice(0).sort((a, b)=>typeToValue2[a.type] - typeToValue2[b.type]))
So given a list of items like so:
item_1 = {id:1, categories: {"category_A" => 1, "category_B" => {"sub_category_A" => 3, "sub_category_B" => 1}}}
item_2 = {id:2, categories: {"category_B" => {"sub_category_A" => 1, "sub_category_B" => 2}}}
Where the numeric value is that items order in a given sub or main category. Now, given a sub or main category, I want to sort the items by the order number. In Ruby I'd write...
# Given category_B and sub_category_A
items.sort_by { |i| i.categories["category_B"]["sub_category_A"] }
# which would return...
[item_2, item_1]
Also want to add, the key is if an item does NOT have the relevant passed category_B and sub_category_A, it should be excluded entirely from output.
You don't need jQuery; JavaScript arrays have a filter() function you can use to limit yourself to valid items and a sort() function that can take a comparing function as its argument:
var item_1 = {
id:1,
categories: {
"category_A" : 1,
"category_B" : {
"sub_category_A" : 3,
"sub_category_B" : 1
}
}
};
var item_2 = {
id:2,
categories: {
"category_B" : {
"sub_category_A" : 1,
"sub_category_B" : 2
}
}
};
var item_3 = {
id: 3,
categories : {
"category_A" : 2
}
};
[item_1,item_2,item_3].filter(function(entry) {
return entry.categories.category_B;}).sort(function(left, right) {
return left.categories["category_B"]["sub_category_A"] -
right.categories["category_B"]["sub_category_A"]
});
// or in the more readable ES6 style
[item_1,item_2,item_3]
.filter((entry) => entry.categories.category_B)
.sort((left, right) => left.categories["category_B"]["sub_category_A"] - right.categories["category_B"]["sub_category_A"]
);
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
You can use Array#Sort in JavaScript.
a.categories["category_B"]["sub_category_A"] - b.categories["category_B"]["sub_category_A"]
Is the comparison of 2 elements in Array.
UPDATE: to excluded from the output. You can use Array#filter it before sorting
Let's see my example
var arr = [{
id:1,
categories:
{
"category_A" : 1,
"category_B" : {
"sub_category_A" : 3,
"sub_category_B" : 1
}
}
},{
id:3,
categories: {
"category_C" : {
"sub_category_A" : 1,
"sub_category_B" : 2
}
}
},{
id:2,
categories: {
"category_B" : {
"sub_category_A" : 1,
"sub_category_B" : 2
}
}
}];
var result = arr.filter(a => a.categories["category_B"])
.sort((a, b) => {
return a.categories["category_B"]["sub_category_A"] - b.categories["category_B"]["sub_category_A"];
})
console.log(result);
Neither ECMAScript nor jQuery have sortBy, but LoDash does and so does Underscore.
It's also not hard to supply your own:
Array.prototype.sortBy = function sortBy(keyFn) {
const compare = (a, b) => a > b ? 1 : (a < b ? -1 : 0);
return this.
map(el => [keyFn(el), el]).
sort(([a, ], [b, ]) => compare(a, b)).
map(([, el]) => el);
};
const item1 = { id: 1, categories: { category_A: 1, category_B: { sub_category_A: 3, sub_category_B: 1 }}};
const item2 = { id: 2, categories: { category_B: { sub_category_A: 1, sub_category_B: 2 }}};
const sorted = [item1, item2].sortBy(el => el.categories.category_B.sub_category_A);
console.log(sorted);