0

I have two sample strings like this.

const country = [{id:1, name:"Mexico"},
                {id:2, name: "Canada"},
                {id:3, name:"Italy"}];

const city = [{id:19, name: "Ottava",country:{id:2, name:"Canada"}},
              {id:23, name:"Roma",country:{id:3, name: "Italy}];

Then I combine these two array

var newArray = country.concat(city);

Later I use this array with select

<Select>
{newArray.map((i) => (
          <Option value={i.country ? i.country.id : i.id}>
            {i.name}
            {i.country && ", " + i.country.name}
          </Option>
        ))}
</Select>

I want to sort the options alphabetically (by country name). I tried many methods. However, I haven't found a solution that I can use without breaking the option values.

As a result, the output I want to get is as follows.

<Option value={2}>Canada</Option>
<Option value={2}>Ottova, Canada</Option>
<Option value={3}>Italy</Option>
<Option value={3}>Roma, Italy</Option>
<Option value={1}>Mexico</Option>
Gucal
  • 842
  • 1
  • 11
  • 19
  • 1
    `city` array has invalid objects. Please add compilable code and the expected output. – adiga May 22 '21 at 11:29
  • Excuse me. I made the necessary correction. however, I just stated that I was waiting for a sequence as output. In short, I want to combine them and sort them by country names. – Gucal May 22 '21 at 11:34
  • Does this answer your question? [Sort array of objects by string property value](https://stackoverflow.com/questions/1129216/sort-array-of-objects-by-string-property-value) – ulou May 22 '21 at 11:35
  • Please add the expected output. Either the sorted `newArray` OR the expected HTML like: ` – adiga May 22 '21 at 11:36
  • I examined it but it didn't work. because the objects are not nested in that question :/ @ulou – Gucal May 22 '21 at 11:39
  • Changed post @adiga – Gucal May 22 '21 at 11:48
  • Now it's much clearer. Voted to reopen. – adiga May 22 '21 at 11:50
  • You just need to sort based on `name` as mentioned in the duplicate: `newArray.sort((a,b) => a.name.localeCompare(b.name))` – adiga May 22 '21 at 11:51
  • It's a problem I've been dealing with for a long time, so I got a little tired. I guess that's why I couldn't explain it clearly. Much better now. Thank you for feedback. – Gucal May 22 '21 at 11:52
  • Sorting using localeCompare sorts not by country name but by name in array unfortunately: /. I want to sort by country name. @adiga – Gucal May 22 '21 at 11:57
  • But, your expected output just has sorting based on name: *"Canada -> Italy -> Mexico -> Ottova, Canada -> Rome"*. If it was sorted by country's name, it would be *"Canada -> Ottova, Canada -> Italy -> Roma, Italy -> Mexico"* – adiga May 22 '21 at 11:58
  • I fixed it later @adiga – Gucal May 22 '21 at 12:01
  • @Gucal - I tried answering your question based on new requirements. – Lakshya Thakur May 22 '21 at 12:08
  • Thank you very much @adiga for your attention. – Gucal May 22 '21 at 12:22

2 Answers2

1

If you need to do it by the name property, just use localCompare(...) inside your sort function's callback and add necessary checks for country and name as well like so :-

const country = [{id:1, name:"Mexico"},
                {id:2, name: "Canada"},
                {id:3, name:"Italy"}];

const city = [{id:19, name: "Ottava",country:{id:2, name:"Canada"}},
              {id:23, name:"Roma",country:{id:3, name: "Italy"}}];

var newArray = country.concat(city);

function comparator(a,b){
let first = a.country?.name ?? a.name;
let second = b.country?.name ?? b.name;
return first.localeCompare(second);
}

console.log(newArray.sort(comparator));
Lakshya Thakur
  • 8,030
  • 1
  • 12
  • 39
  • Hi, thank you for attention. But unfortunately my ranking has not changed :( "Canada -> Italy -> Mexico -> Ottova, Canada -> Rome" sorted again. I want it like this. Canada -> Ottova, Canada -> Italy -> Roma, Italy -> Mexico – Gucal May 22 '21 at 12:11
  • But the `newArray` is sorted in the order you want. Now it's just using `map` on it and it should work. Try `newOrder.sort(comparator).map` and see. – Lakshya Thakur May 22 '21 at 12:14
1

One approach is to map these arrays to objects with same properties including a txt property to use for render.

In the cityOpts array do the name/country concatenation.

Then in render they will all be the same structural format

const country=[{id:1,name:"Mexico"},{id:2,name:"Canada"},{id:3,name:"Italy"}],city=[{id:19,name:"Ottava",country:{id:2,name:"Canada"}},{id:23,name:"Roma",country:{id:3,name:"Italy"}}];
              
 const countryOpts = country.map(({id, name})=> ({id,  country: name, txt: name}))
 
 const cityOpts = city.map(({id, name, country}) => ({id, country:country.name, txt: `${name}, ${country.name}`}))
 
 const newArray = [...countryOpts,...cityOpts].sort((a,b) => a.country.localeCompare(b.country))
 
 console.log(newArray)

Render would be simplified to

<Select>
{newArray.map((i) => <Option value={i.id}>{i.txt}</Option> )}
</Select>
charlietfl
  • 170,828
  • 13
  • 121
  • 150