Multiply/Clone multiple times objects inside and array in JavaScript - javascript

Having an array of objects in this format:
const data = [
{country_id: 1, country: "Greece", value: 3},
{country_id: 2, country: "Cyprus", value: 2},
{country_id: 3, country: "Turkey", value: 4}
]
how can I multiply/clone each of the objects to get the following array of objects using JavaScript? I want to multiply each object as many times as specified in value and get a new array.
const new_data = [
{id: 1, country_id: 1, country: "Greece", value: 3},
{id: 2, country_id: 1, country: "Greece", value: 3},
{id: 3, country_id: 1, country: "Greece", value: 3},
{id: 4, country_id: 2, country: "Cyprus", value: 2},
{id: 5, country_id: 2, country: "Cyprus", value: 2},
{id: 6, country_id: 3, country: "Turkey", value: 4},
{id: 7, country_id: 3, country: "Turkey", value: 4},
{id: 8, country_id: 3, country: "Turkey", value: 4},
{id: 9, country_id: 3, country: "Turkey", value: 4}
]
My best attempt so far is this one using Object.assign but unfortunately the map returns the same array as in data:
const new_data = data.map((d, i) => {
for (var i = 0; i < d.mult; ++i) {
Object.assign({}, d[i]);
}
return d;
})

You could do it like this, where you simply fill an array with value elements, and map it to a clone of the original element:
const data = [
{country_id: 1, country: "Greece", value: 3},
{country_id: 2, country: "Cyprus", value: 2},
{country_id: 3, country: "Turkey", value: 4}
]
console.log(
data.flatMap((el) => new Array(el.value).fill(null).map(e => ({...el}))))

You can do it using Array.from() and reduce. Try this-
Here you can create a temporary array using Array.from({length: X}). Here the {length: X} object says the from method to create an array with the length of X.
After that, the callback function of the Array.from() method returns the current item for every iteration. That's it.
const data = [
{country_id: 1, country: "Greece", value: 3},
{country_id: 2, country: "Cyprus", value: 2},
{country_id: 3, country: "Turkey", value: 4}
];
const res = data.reduce((acc, curr) => {
const tmp = Array.from({length: curr.value}, () => curr);
acc.push(...tmp);
return acc;
}, []);
console.log(JSON.stringify(res));
.as-console-wrapper{min-height: 100%!important; top: 0}

The easiest would be to just create a new array, loop through the old one and add duplicates (with the new id field) to the result array:
const data = [
{ country_id: 1, country: "Greece", value: 3 },
{ country_id: 2, country: "Cyprus", value: 2 },
{ country_id: 3, country: "Turkey", value: 4 }
]
const result = [];
let id = 0;
for (const row of data) {
for (let i = 0; i < row.value; i++) {
const newRow = {
...row, // copy old data
id: ++id, // but set this field and increase `id`
};
result.push(newRow);
}
}
console.log(result);
If you want to use something like .map, you'd need to use .flatMap or .map followed by .flat(). Your (flat)map would return an array with the value new rows. You'd still have to fix the country_id though.

Related

delete an object in an array based on index at level 1 and level 2

I have a array of object which has sub arrays, I want to delete the subarray object based on the index from both the levels.
For example if want to delete object with id:12 I should pass firstIndex = 0 and secondIndex = 1;
Pls note above mentioned eg: is for better understanding of question and not to delete element by id should be deleted based on index only(use case is different and index are received from different source).
const array = [
{
id: 1,
row: [
{id: 11, value: x},
{id: 12, value: y},
{id: 13, value: z},
],
},
{
id: 2,
row: [
{id: 21, value: a},
{id: 22, value: b},
{id: 23, value: c},
],
}
]
firstIndex = 1, secondIndex = 2
const result = [
{
id: 1,
row: [
{id: 11, value: x},
{id: 12, value: y},
{id: 13, value: z},
],
},
{
id: 2,
row: [
{id: 21, value: a},
{id: 22, value: b},
],
}
]
firstIndex = 0, secondIndex = 1
const result = [
{
id: 1,
row: [
{id: 11, value: x},
{id: 13, value: z},
],
},
{
id: 2,
row: [
{id: 21, value: a},
{id: 22, value: b},
{id: 23, value: c},
],
}
]
const result = array.map((el, i) => (i === firstIndex) && el.rows.map(elm, (elm, index) => (index === secondIndex ) && elm.splice(index, 1)))
You can remove Item from array by splice method like this:
const array = [
{
id: 1,
row: [
{id: 11, value: 'x'},
{id: 12, value: 'y'},
{id: 13, value: 'z'},
],
},
{
id: 2,
row: [
{id: 21, value: 'a'},
{id: 22, value: 'b'},
{id: 23, value: 'c'},
],
}
]
firstIndex = 1, secondIndex = 2;
array[firstIndex].row.splice(secondIndex, 1);
console.log(array)
By adding the optional chaining: ? you assure that the code works even if your indexes are wrong, it then just doesn't change anything and you won't get an error
const array = [
{ id: 1, row: [{ id: 11, value: "x" }, { id: 12, value: "y" }, { id: 13, value: "z" }, ], },
{ id: 2, row: [{ id: 21, value: "a" }, { id: 22, value: "b" }, { id: 23, value: "c" }, ], }
]
function deleteByIndex(array, index1, index2) {
array[index1]?.row?.splice(index2, 1)
return array
}
console.log(deleteByIndex(array, 1, 2))

How to Create Arrays of Objects With A Common Prop Value

Let's say I have an array of objects like:
flattenedObjects = [
{name: 'Bill', city: 1},
{name: 'Guillermo', city: 1},
{name: 'Wilhem', city: 1},
{name: 'William', city: 1},
{name: 'Nick', city: 2},
{name: 'Nicolas', city: 2},
{name: 'Nicholas', city: 2},
{name: 'Rick', city: 3}
]
I want to create individual arrays of objects grouped by "city". In the code, I will also deconstruct each object so that the final output will be:
boston = ['Bill', 'Guillermo', 'Wilhelm', 'William']
miami = ['Nick', 'Nickolas', 'Nicholas']
london = ['Rick']
I am having difficulties creating the grouped array of objects.
I can do it with one single object, as such:
let boston = flattenedObjects.filter(function (obj) {
return obj.city == 1;
});
What I was thinking of doing was to take a iterate through an object and filtering dynamically, like so:
let cities = {
boston: 1,
miami: 2,
london: 3
}
And then trying something like:
let newObj = flattenedObjects.filter(function (x) {
let obj = {};
Object.entries(cities).forEach(([key, value]) => {
obj["name"] = `${key}`;
obj["city"] = x.city == `${value}`;
return obj;
});
});
This isn't consoling what is expected. It's just an array of objects very similar to the "obj" up above.
let flattenedObjects = [
{name: 'Bill',city: 1},
{name: 'Guillermo',city: 1},
{name: 'Wilhem',city: 1},
{name: 'William',city: 1},
{name: 'Nick',city: 2},
{name: 'Nicolas',city: 2},
{name: 'Nicholas',city: 2},
{name: 'Rick',city: 3}
];
let cities = {
boston: 1,
miami: 2,
london: 3
}
let data = {}
for (const [key, value] of Object.entries(cities)) {
data[key] = flattenedObjects.filter(p => p.city === value).map(e => e.name);
}
console.log(data)
Create a reverse map of city code to city name, O(1) constant time lookups.
Reduce the flattenedObjects array into an object using the city name as a key and generate an array fo the names, O(n) linear access.
const flattenedObjects = [
{ name: "Bill", city: 1 },
{ name: "Guillermo", city: 1 },
{ name: "Wilhem", city: 1 },
{ name: "William", city: 1 },
{ name: "Nick", city: 2 },
{ name: "Nicolas", city: 2 },
{ name: "Nicholas", city: 2 },
{ name: "Rick", city: 3 }
];
const cities = {
boston: 1,
miami: 2,
london: 3
};
const citiesByCode = Object.fromEntries(
Object.entries(cities).map(([city, code]) => [code, city])
);
const groupedResult = flattenedObjects.reduce((groups, current) => {
const cityCode = citiesByCode[current.city];
if (!groups[cityCode]) groups[cityCode] = [];
groups[cityCode].push(current.name);
return groups;
}, {});
console.log(groupedResult);
You could use a reduce statement to reduce the flattenedObjects array into a single object in the format that you want.
const flattenedObjects = [
{name: 'Bill', city: 1},
{name: 'Guillermo', city: 1},
{name: 'Wilhem', city: 1},
{name: 'William', city: 1},
{name: 'Nick', city: 2},
{name: 'Nicolas', city: 2},
{name: 'Nicholas', city: 2},
{name: 'Rick', city: 3},
];
// the keys are the city number rather than city name
const cities = {
1: 'boston',
2: 'miami',
3: 'london',
};
const obj = flattenedObjects.reduce((o, flattenedObject) => {
const cityName = cities[flattenedObject.city];
if (o[cityName] === undefined) {
o[cityName] = [];
}
o[cityName].push(flattenedObject.name);
return o;
}, {});
console.log(obj);

Converting array of object to hierarchical data structure

I have an original array and I want to plot it in Sunburst map which needs a hierarchical data structure.
[
{id: "Asia,India,NewDelhi", value: 41},
{id: "Europe,Germany,Berlin", value: 24},
{id: "Europe,England,London", value: 3},
{id: "NorthAmerica,USA,NewYork", value: 4},
{id: "NorthAmerica,USA,Boston", value: 3},
{id: "NorthAmerica,USA,chicago", value: 3},
{id: "Austrailia,Sydney", value: 4},
{id: "Asia,China,Beijing", value: 2},
]
Desired Result
[
{
id: Asia,
children:[{
id: India,
children:[{
id: Delhi,
value: 41,
}]
},
{
id:China,
children:[{
id: Beijing
value: 2,
}]
}]
},
{
id: Europe,
children: [{
id: Germany,
children: [{
id: Berlin,
value: 24,
}]
},
{
id: England,
children: [{
id: London,
value: 3,
}]
}]
},
{
id: NorthAmerica,
children:[{
id: USA,
children:[{
id: NewYork,
value: 4,
},
{
id: Boston,
value: 3,
},
{
id: Chicago,
value: 3,
}]
}]
},
{
id: Austrailia
children: [{
id:Sydney,
value: 4,
}]
},
]
can anyone help me with this, I tried using reduce method but I am not able to get the desired result.
PS : It would be super useful if anyone could suggest an answer that would deal with n number of ids separated by commas. For ex: here we have 3 id hierarchy separated by commas, what would happen if there were 4 or 5 depth data.
A simple solution with recursion:
const data = [
{id: "Asia,India,NewDelhi", value: 41},
{id: "Europe,Germany,Berlin", value: 24},
{id: "Europe,England,London", value: 3},
{id: "NorthAmerica,USA,NewYork", value: 4},
{id: "NorthAmerica,USA,Boston", value: 3},
{id: "NorthAmerica,USA,Chicago", value: 3},
{id: "Austrailia,Sydney", value: 4},
{id: "Asia,China,Beijing", value: 2},
];
const addChild = (ids, value, arr) => {
const id = ids.shift();
let index = arr.findIndex(item => item.id === id);
if (index < 0) {
arr.push({id, children: []});
index = arr.length - 1;
}
if (ids.length > 0) {
const children = arr[index].children;
addChild(ids, value, children);
}
else
arr[index].value = value;
}
const treeData = data.reduce((tree, item) => {
const ids = item.id.split(',');
addChild(ids, item.value, tree);
return tree;
}, []);
console.log(treeData);
To build a hierarchy of objects from your input is fairly straightforward, you dont even need to do anything recursive a loop + reduce will do it. This will work with any number of levels in your comma separated list.
const input = [
{id: "Asia,India,NewDelhi", value: 41},
{id: "Europe,Germany,Berlin", value: 24},
{id: "Europe,England,London", value: 3},
{id: "NorthAmerica,USA,NewYork", value: 4},
{id: "NorthAmerica,USA,Boston", value: 3},
{id: "NorthAmerica,USA,chicago", value: 3},
{id: "Austrailia,Sydney", value: 4},
{id: "Asia,China,Beijing", value: 2}
]
const result = input.map(o => ({ids:o.id.split(","), value:o.value})).reduce( (acc,obj) => {
let curr = acc;
let id;
while( (id = obj.ids.shift()) != null ){
if(!curr[id])
curr[id] = {};
curr = curr[id];
}
curr.value = obj.value
return acc;
},{});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
To then turn this into the format you wanted does take a bit of recursion:
const input = [
{id: "Asia,India,NewDelhi", value: 41},
{id: "Europe,Germany,Berlin", value: 24},
{id: "Europe,England,London", value: 3},
{id: "NorthAmerica,USA,NewYork", value: 4},
{id: "NorthAmerica,USA,Boston", value: 3},
{id: "NorthAmerica,USA,chicago", value: 3},
{id: "Austrailia,Sydney", value: 4},
{id: "Asia,China,Beijing", value: 2}
]
const result = input.map(o => ({ids:o.id.split(","), value:o.value})).reduce( (acc,obj) => {
let curr = acc;
let id;
while( (id = obj.ids.shift()) != null ){
if(!curr[id])
curr[id] = {};
curr = curr[id];
}
curr.value = obj.value
return acc;
},{});
function buildHierarchy(input){
return Object.entries(input).map( ([id,children]) => {
if(children.value){
return {id,value:children.value}
}
return {id, children: buildHierarchy(children)}
})
}
console.log(buildHierarchy(result));
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to sum the same objects in array

It is possible to sum the values of an array if they are the same like this:
var COLLECTION = [
{
"coords":[1335,2525],
"items":[
{id: "boletus",qty: 1},
{id: "lepiota",qty: 3},
{id: "boletus",qty: 2},
{id: "lepiota",qty: 4},
{id: "carbonite",qty: 4},
],
},
{
"coords":[1532,2889],
"items":[
{id: "boletus",qty: 2},
{id: "lepiota",qty: 6},
{id: "boletus",qty: 1},
{id: "lepiota",qty: 4},
{id: "chamomile",qty: 4},
],
}]
To return something like this:
var COLLECTION = [
{
"coords":[1335,2525],
"items":[
{id: "boletus",qty: 3},
{id: "lepiota",qty: 7},
{id: "carbonite",qty: 4},
],
},
{
"coords":[1532,2889],
"items":[
{id: "boletus",qty: 3},
{id: "lepiota",qty: 10},
{id: "chamomile",qty: 4},
],
}]
Wihout losing the other parts of the array?
(doing by hand is hard because I have more than 10 thousand duplicates like the example above, and the array have 600 thousand entries.
You could use map() to create new array and inside reduce() to group items objects by id and sum qty.
var data = [{"coords":[1335,2525],"items":[{"id":"boletus","qty":1},{"id":"lepiota","qty":3},{"id":"boletus","qty":2},{"id":"lepiota","qty":4},{"id":"carbonite","qty":4}]},{"coords":[1532,2889],"items":[{"id":"boletus","qty":2},{"id":"lepiota","qty":6},{"id":"boletus","qty":1},{"id":"lepiota","qty":4},{"id":"chamomile","qty":4}]}]
const result = data.map(function({coords, items}) {
return {coords, items: Object.values(items.reduce(function(r, e) {
if(!r[e.id]) r[e.id] = Object.assign({}, e)
else r[e.id].qty += e.qty
return r;
}, {}))}
})
console.log(result)
You could take the power of Map and render the result by using Array.from with a mapping function which builds new objects for items.
var COLLECTION = [{ coords: [1335, 2525], items: [{ id: "boletus", qty: 1 }, { id: "lepiota", qty: 3 }, { id: "boletus", qty: 2 }, { id: "lepiota", qty: 4 }, { id: "carbonite", qty: 4 }], }, { coords: [1532, 2889], items: [{ id: "boletus", qty: 2 }, { id: "lepiota", qty: 6 }, { id: "boletus", qty: 1 }, { id: "lepiota", qty: 4 }, { id: "chamomile", qty: 4 }] }];
COLLECTION.forEach(o => {
var map = new Map;
o.items.forEach(({ id, qty }) => map.set(id, (map.get(id) || 0) + qty));
o.items = Array.from(map, ([id, qty]) => ({ id, qty }));
});
console.log(COLLECTION);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use the functions forEach and reduce
This approach mutates the original array
var COLLECTION = [ { "coords":[1335,2525], "items":[ {id: "boletus",qty: 1}, {id: "lepiota",qty: 3}, {id: "boletus",qty: 2}, {id: "lepiota",qty: 4}, {id: "carbonite",qty: 4}, ], }, { "coords":[1532,2889], "items":[ {id: "boletus",qty: 2}, {id: "lepiota",qty: 6}, {id: "boletus",qty: 1}, {id: "lepiota",qty: 4}, {id: "chamomile",qty: 4}, ], }];
COLLECTION.forEach((o) => {
o.items = Object.values(o.items.reduce((a, c) => {
(a[c.id] || (a[c.id] = {id: c.id, qty: 0})).qty += c.qty;
return a;
}, {}));
});
console.log(COLLECTION);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you want to create a new array and keep the original data:
This approach uses the function map to create a new "cloned" array.
var COLLECTION = [ { "coords":[1335,2525], "items":[ {id: "boletus",qty: 1}, {id: "lepiota",qty: 3}, {id: "boletus",qty: 2}, {id: "lepiota",qty: 4}, {id: "carbonite",qty: 4}, ], }, { "coords":[1532,2889], "items":[ {id: "boletus",qty: 2}, {id: "lepiota",qty: 6}, {id: "boletus",qty: 1}, {id: "lepiota",qty: 4}, {id: "chamomile",qty: 4}, ] }],
result = COLLECTION.map(o => o);
result.forEach((o) => {
o.items = Object.values(o.items.reduce((a, c) => {
(a[c.id] || (a[c.id] = {id: c.id, qty: 0})).qty += c.qty;
return a;
}, {}));
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Javascript Sort by Object Value that has Multiple Meanings (group) with only Comparartor

I have some data formatted like so:
var data = [
{ id: 1, name: "Netherlands", population: 17},
{ id: 1.1, name: "Rotterdam", population: 4},
{ id: 1.2, name: "Amsterdam", population: 2},
{ id: 2, name: "USA", population: 350},
{ id: 3, name: "Germany", population: 55},
{ id: 3.1, name: "Berlin", population: 4},
{ id: 3.2, name: "Stuttgard", population: 3},
{ id: 3.3, name: "Cologne", population: 3},
{ id: 4, name: "UK", population: 60},
{ id: 5, name: "Canada", population: 30},
{ id: 5.1, name: "Ottawa", population: 2},
];
Basically name can be either city or country. Countries are identified as whole numbers and their cities as .1. Each country can have 0-3 cities.
Is it possible to write a comparator function for Array.prototype.sort where it sorts for instance, the countries alphabetically, then by their cities but keeps the country/city grouping intact, and does not mutate the data array beforehand? Is this possible? If not, why? If it is not possible, I'd still be interested to know how to do this mutating the array beforehand, though it will require a lot more backtracking for me, and is not ideal.
So sorting alphabetically by name would give:
var data = [
{ id: 5, name: "Canada", population: 30},
{ id: 5.1, name: "Ottawa", population: 2},
{ id: 3, name: "Germany", population: 55},
{ id: 3.1, name: "Berlin", population: 4},
{ id: 3.3, name: "Cologne", population: 3},
{ id: 3.2, name: "Stuttgard", population: 3},
{ id: 1, name: "Netherlands", population: 17},
{ id: 1.2, name: "Amsterdam", population: 2},
{ id: 1.1, name: "Rotterdam", population: 4},
{ id: 4, name: "UK", population: 60},
{ id: 2, name: "USA", population: 350},
];
Here is a plunker, with how far I have gotten:
http://plnkr.co/edit/vPTaoh
I don't understand why/how array.prototype.sort picks which values to compare, so I'm not sure what direction to go. Any help would be greatly appreciated.
I don't understand why/how Array.prototype.sort picks which values to compare
It picks them arbitrarily from the array, depending on what sorting algorithm it uses internally. Your comparison function needs to work on all values.
Is it possible to write a comparator function for Array.prototype.sort where it sorts for instance, the countries alphabetically, then by their cities but keeps the country/city grouping intact, and does not mutate the data array beforehand?
No, this is not (easily) possible. To compare two items, you always need to compare their countries first, but that's not possible whent the item is a city: there is no link from the city to the country name.
You would need to find a way to look this up first (technically it's possible given the id, but you have to construct a lookup table beforehand or inefficiently search the array during sorting).
You would do yourself a favour by changing your data to the following structure:
var data = [
{id: 1, name: "Netherlands", population: 17, cities: [
{id: 1.1, name: "Rotterdam", population: 4},
{id: 1.2, name: "Amsterdam", population: 2}
]},
{id: 2, name: "USA", population: 350, cities: []},
{id: 3, name: "Germany", population: 55, cities: [
{id: 3.1, name: "Berlin", population: 4},
{id: 3.2, name: "Stuttgard", population: 3},
{id: 3.3, name: "Cologne", population: 3}
]},
{id: 4, name: "UK", population: 60, cities: []},
{id: 5, name: "Canada", population: 30, cities: [
{id: 5.1, name: "Ottawa", population: 2}
]}
];
And then simply sort the countries only and the cities within them using the standard approach.
Array.prototype.sort picks any values it needs to to compare, so you have to be prepared to return the ordering for e.g. 3.1 vs. 1.2, which will require looking up the country name.
function compare(a, b) {
const countryAName = array.find(elt => elt.id === countryA).name;
const countryBName = array.find(elt => elt.id === countryB).name;
return countryAName < countryBName ? -1 :
countryAName > countryBName ? +1 :
a.name < b.name ? -1 :
a.name > b.name ? +1 : 0;
}
As Bergi correctly points out, the array.find in the comparator is going to be pretty inefficient. So you might want to preprocess the array to include the country in each item in the array:
array.forEach(elt => elt.country = array.find(e => e.id === Math.floor(elt.id)).name);
Then you can write the comparator as:
function compare(a, b) {
return a.country < b.country ? -1 :
a.country > b.country ? +1 :
a.name < b.name ? -1 :
a.name > b.name ? +1 : 0;
}
If you would prefer not to "mutate" the array by adding the country property to each element, then you could put it in a separate lookup table:
const map = [];
array.forEach(elt => { if (!(elt.id % 1)) map[Math.floor(elt.id)] = elt.name; });
Then write the comparator as
function compare(a, b) {
const countryAName = map[Math.floor(a.id)];
const countryBName = map[Math.floor(b.id)];
return countryAName < countryBName ? -1 :
countryAName > countryBName ? +1 :
a.name < b.name ? -1 :
a.name > b.name ? +1 : 0;
}
Without any hash this code works perfectly well on FF but cracks Chrome (or any recent WebKit browser like Opera) for no reason that i can think of. It's been already late and i have spent last hour desperately checking types or so without any luck. I would love if somebody could point out what's going wrong in Chrome?
var data = [
{ id: 1, name: "Netherlands", population: 17},
{ id: 1.1, name: "Rotterdam", population: 4},
{ id: 1.2, name: "Amsterdam", population: 2},
{ id: 2, name: "USA", population: 350},
{ id: 3, name: "Germany", population: 55},
{ id: 3.1, name: "Berlin", population: 4},
{ id: 3.2, name: "Stuttgard", population: 3},
{ id: 3.3, name: "Cologne", population: 3},
{ id: 4, name: "UK", population: 60},
{ id: 5, name: "Canada", population: 30},
{ id: 5.1, name: "Ottawa", population: 2},
],
sorted = data.sort((a,b) => a.id%1 === 0 && b.id%1 === 0 ? a.name.localeCompare(b.name)
: ~~a.id === ~~b.id ? a.id - b.id
: data.find(o => o.id === ~~a.id).name.localeCompare(data.find(o => o.id === ~~b.id).name));
console.log(sorted);
Well interesting really.. As per #Bergi's comment i did try & catch # Chrome and logged the data array when the error is caught. However once the error is swallowed by catch it returned the result perfectly though. Check this up on Chrome.
var data = [
{ id: 1, name: "Netherlands", population: 17},
{ id: 1.1, name: "Rotterdam", population: 4},
{ id: 1.2, name: "Amsterdam", population: 2},
{ id: 2, name: "USA", population: 350},
{ id: 3, name: "Germany", population: 55},
{ id: 3.1, name: "Berlin", population: 4},
{ id: 3.2, name: "Stuttgard", population: 3},
{ id: 3.3, name: "Cologne", population: 3},
{ id: 4, name: "UK", population: 60},
{ id: 5, name: "Canada", population: 30},
{ id: 5.1, name: "Ottawa", population: 2},
],
sorted = data.sort(function(a,b){
try {
return a.id%1 === 0 && b.id%1 === 0 ? a.name.localeCompare(b.name)
: ~~a.id === ~~b.id ? a.id - b.id
: data.find(o => o.id === ~~a.id).name.localeCompare(data.find(o => o.id === ~~b.id).name);
}
catch(e) {console.log(JSON.stringify(data)) }
});
console.log(sorted);
You could use a hash table for the country name object and some calculation for moving the country up.
var data = [{ id: 1, name: "Netherlands", population: 17 }, { id: 1.1, name: "Rotterdam", population: 4 }, { id: 1.2, name: "Amsterdam", population: 2 }, { id: 2, name: "USA", population: 350 }, { id: 3, name: "Germany", population: 55 }, { id: 3.1, name: "Berlin", population: 4 }, { id: 3.2, name: "Stuttgart", population: 3 }, { id: 3.3, name: "Cologne", population: 3 }, { id: 4, name: "UK", population: 60 }, { id: 5, name: "Canada", population: 30 }, { id: 5.1, name: "Ottawa", population: 2 }, ],
hash = {};
data.forEach(function (a) {
if (a.id === Math.floor(a.id)) {
hash[a.id] = a.name;
}
});
data.sort(function (a, b) {
function getHash(o) { return hash[Math.floor(o.id)]; }
return (
getHash(a).localeCompare(getHash(b)) || // compare country
(Math.floor(a.id) !== a.id) - (Math.floor(b.id) !== b.id) || // move int to top
a.name.localeCompare(b.name) // compare city
);
});
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories