Reputation: 1253
I'm getting some json in the following structure:
[
{
"name": "First object",
"info": [
"2 people",
"this is a description"
]
},
{
"name": "Second object",
"info": [
"this is a description",
"1 furniture"
]
},
{
"name": "Third object",
"info": [
"3 animals",
"this is a description"
]
},
]
I know how to sort arrays by values (like if I was supposed to sort only by name
), but how can I sort it based on the number in the array? They will not always be at the same index, so I guess I will need a sorting routine to put them in the right place or something. The array value with a number will be the only value containing a digit. And that will be used for sorting. So the output should be:
1 furniture - Second object
2 people - First object
3 animals - Third object
Will I have to traverse the whole array, or is there a simpler way to do it? Any help would be appreciated so I'm able to wrap my head around it.
Upvotes: -1
Views: 73
Reputation: 386756
You could parse the values and take the truty ones for getting the delta.
const
array = [{ name: "First object", info: ["2 people", "this is a description"] }, { name: "Second object", info: ["this is a description", "1 furniture"] }, { name: "Third object", info: ["3 animals", "this is a description"] }];
array.sort(({ info: [a0, a1] }, { info: [b0, b1] }) =>
(parseInt(a0, 10) || parseInt(a1, 10)) -
(parseInt(b0, 10) || parseInt(b1, 10))
);
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 0
Reputation: 23597
If you are interested in performance you can sort without intermediate arrays with caching found numbers in a map:
const arr = [{"name":"First object","info":["2 people","this is a description"]},{"name":"Second object","info":["this is a description","1 furniture"]},{"name":"Third object","info":["3 animals","this is a description"]}]
const map = new Map;
const getNumber = item => {
let num = map.get(item);
if(num) return num;
num = Infinity;
for(const line of item.info){
num = parseInt(line);
if(num === num){
break;
}
}
map.set(item, num);
return num;
}
arr.sort((a, b) => {
a = getNumber(a), b = getNumber(b);
return a > b ? 1 : a < b ? -1 : 0;
});
console.log(arr);
` Chrome/133
---------------------------------------------------------------------------------------------------
> n=3 | n=30 | n=300 | n=3000
Alexander 1.25x x1m 222 | ■ 1.00x x1m 683 | ■ 1.00x x100k 461 | ■ 1.00x x10k 445
Alexander (no caching) ■ 1.00x x1m 178 | 2.53x x100k 173 | 3.71x x10k 171 | 3.80x x1k 169
Ori 1.46x x1m 260 | 3.60x x100k 246 | 6.40x x10k 295 | 7.03x x1k 313
Carsten 1.89x x1m 336 | 4.61x x100k 315 | 7.42x x10k 342 | 8.40x x1k 374
--------------------------------------------------------------------------------------------------- `
const $chunk = [{"name":"First object","info":["2 people","this is a description"]},{"name":"Second object","info":["this is a description","1 furniture"]},{"name":"Third object","info":["3 animals","this is a description"]}]
const $input = [];
const arr = $input;
// @benchmark Alexander
{
const map = new Map;
const getNumber = item => {
let num = map.get(item);
if(num) return num;
num = Infinity;
for(const line of item.info){
num = parseInt(line);
if(num === num){
break;
}
}
map.set(item, num);
return num;
}
arr.sort((a, b) => {
a = getNumber(a), b = getNumber(b);
return a > b ? 1 : a < b ? -1 : 0;
});
}
// @benchmark Alexander (no caching)
{
const getNumber = item => {
let num = Infinity;
for(const line of item.info){
num = parseInt(line);
if(num === num){
return num;
}
}
return num;
}
arr.sort((a, b) => {
a = getNumber(a), b = getNumber(b);
return a > b ? 1 : a < b ? -1 : 0;
});
}
// @benchmark Ori
{
const getNumber = arr =>
parseInt(arr.find(s => !Number.isNaN(parseInt(s, 10))) ?? 0, 10)
arr
.map(o => [getNumber(o.info), o]) // decorate
.sort(([a], [b]) => a - b) // sort
.map(([, o]) => o) // undecorate
}
// @benchmark Carsten
const perm=arr.map((e,i)=>[parseInt(e.info.find(f=>f.match(/^\d/)),10),i]).sort(([a],[b])=>a-b);
perm.map(([_,i])=>arr[i])
/*@skip*/ fetch('https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js').then(r => r.text().then(eval));
Upvotes: 0
Reputation: 28226
Another way could be to create a permutation array perm
and use that to list the elements of arr
in the desired order:
const arr = [{"name":"First object","info":["2 people","this is a description"]},{"name":"Second object","info":["this is a description","1 furniture"]},{"name":"Third object","info":["3 animals","this is a description"]}]
const perm=arr.map((e,i)=>[parseInt(e.info.find(f=>f.match(/^\d/)),10),i]).sort(([a],[b])=>a-b);
console.log("permutation array:",perm);
console.log("result:",perm.map(([_,i])=>arr[i]))
Upvotes: 0
Reputation: 192607
Use a Schwartzian transform also known as DSU (decorate, sort, undecorate):
const arr = [{"name":"First object","info":["2 people","this is a description"]},{"name":"Second object","info":["this is a description","1 furniture"]},{"name":"Third object","info":["3 animals","this is a description"]}]
// get the number from the array of strings, assuming that the number is always 1st in a string
const getNumber = arr =>
parseInt(arr.find(s => !Number.isNaN(parseInt(s, 10))) ?? 0, 10)
const result = arr
.map(o => [getNumber(o.info), o]) // decorate
.sort(([a], [b]) => a - b) // sort
.map(([, o]) => o) // undecorate
console.log(result)
Upvotes: 1
Reputation: 48
As @Barmar said it would probably be best to store that data in an object, not an array. However, if you get the data in such structure from an API you can go about it in a few ways.
Parse the data:
array = JSON.parse(`[
{
"name": "First object",
"info": [
{"amount": "2 people"},
{"description": "this is a description"}
]
},
{
"name": "Second object",
"info": [
{"description": "this is a description"},
{"amount": "1 furniture"}
]
},
{
"name": "Third object",
"info": [
{"amount": "3 animals"},
{"description": "this is a description"}
]
}
]`)
You're probably gonna use it later so might as well map it to flat objects:
flat_array = array.map(x => Object.assign({ name: x.name }, ...x.info)
Then you can sort it by simply doing:
flat_array.sort((a, b) => a.amount.localeCompare(b.amount))
Otherwise, if you just want to sort the data, without modifying it, you can do:
array.sort((a, b) => Object.assign({}, ...a.info).amount.localeCompare(Object.assign({}, ...b.info).amount))
following the Object.assign
example. Or as other people suggested in the comments:
array.sort((a, b) => a.info.find(x => "amount" in x).amount.localeCompare(b.info.find(x => "amount" in x).amount))
Upvotes: 0