(मनमाने ढंग से) क्षेत्र के नामों की संरचना की एक सरणी को क्रमबद्ध करने का सबसे छोटा तरीका क्या है?


129

मुझे बस एक समस्या थी जहां मेरे पास एक संरचना थी, उदाहरण के लिए

package main

import "log"

type Planet struct {
    Name       string  `json:"name"`
    Aphelion   float64 `json:"aphelion"`   // in million km
    Perihelion float64 `json:"perihelion"` // in million km
    Axis       int64   `json:"Axis"`       // in km
    Radius     float64 `json:"radius"`
}

func main() {
    var mars = new(Planet)
    mars.Name = "Mars"
    mars.Aphelion = 249.2
    mars.Perihelion = 206.7
    mars.Axis = 227939100
    mars.Radius = 3389.5

    var earth = new(Planet)
    earth.Name = "Earth"
    earth.Aphelion = 151.930
    earth.Perihelion = 147.095
    earth.Axis = 149598261
    earth.Radius = 6371.0

    var venus = new(Planet)
    venus.Name = "Venus"
    venus.Aphelion = 108.939
    venus.Perihelion = 107.477
    venus.Axis = 108208000
    venus.Radius = 6051.8

    planets := [...]Planet{*mars, *venus, *earth}
    log.Println(planets)
}

कहते हैं कि आप इसे क्रमबद्ध करना चाहते हैं Axis। आप उसे कैसे करते हैं?

(नोट: मैंने http://golang.org/pkg/sort/ देखा है और यह काम करने लगता है, लेकिन मुझे लगभग 20 पंक्तियाँ बस एक साधारण सी कुंजी द्वारा सरल छंटाई के लिए जोड़नी हैं। मेरी एक अजगर पृष्ठभूमि है जहाँ यह है। जितना सरल है sorted(planets, key=lambda n: n.Axis)- क्या गो में भी कुछ ऐसा ही सरल है? '


यहां एक और तृतीय पक्ष github.com/patrickmn/sortutil पैकेज है। यह सामान्य छंटाई कर सकता है और घोंसले की छंटाई भी कर सकता है। यहाँ मैं प्रदर्शन के बारे में दस्तावेज़ीकरण से उद्धृत करता हूं: "जबकि सॉर्टुटिल सुविधाजनक है, यह एक समर्पित प्रकार को हरा नहीं सकता है। प्रदर्शन के मामले में इंटरफ़ेस। सॉर्टिंग को लागू करना। एक प्रकार के इंटरफ़ेस। बाइनाम के लिए इंटरफ़ेस, जो एम्बेड करता है जैसे [] MyStruct और Sort.Sort। (ByName {MySlice}) पर विचार किया जाना चाहिए जब उच्च प्रदर्शन की आवश्यकता हो। "
तुतोम्पीटा

जवाबों:


63

अद्यतन: यह उत्तर पुराने संस्करणों से संबंधित है go। गो 1.8 और नए के लिए, नीचे आंद्रेकेआर का उत्तर देखें


यदि आप मानक लाइब्रेरी sortपैकेज की तुलना में कुछ कम क्रिया चाहते हैं , तो आप तीसरे पक्ष के github.com/bradfitz/sliceपैकेज का उपयोग कर सकते हैं । यह आपके स्लाइस को सॉर्ट करने के लिए आवश्यक तरीकों Lenऔर उत्पन्न करने के लिए कुछ ट्रिक्स का उपयोग करता है Swap, इसलिए आपको केवल एक Lessविधि प्रदान करने की आवश्यकता है ।

इस पैकेज के साथ, आप इस प्रकार का प्रदर्शन कर सकते हैं:

slice.Sort(planets[:], func(i, j int) bool {
    return planets[i].Axis < planets[j].Axis
})

planets[:]हिस्सा अपने सरणी को कवर एक टुकड़ा का उत्पादन करने के लिए आवश्यक है। यदि आप planetsएक सरणी के बजाय एक टुकड़ा बनाते हैं तो आप उस हिस्से को छोड़ सकते हैं।


28
मुझे सरणी को सॉर्ट करने के लिए तीसरे पक्ष के पैकेज का उपयोग करना होगा (जब तक कि मैं वर्बोज़ कोड की अविश्वसनीय मात्रा नहीं लिखना चाहता)? इस भाषा में क्या गलत है? मेरा मतलब है ... यह सिर्फ तरह है! कोई काला जादू नहीं।
जेन्दास

8
@ जेंदास गो का मतलब सरल होना है, आसान नहीं। रूबी आसान है। यहां तक ​​कि जब बिल्कुल नहीं पता कि कुछ कैसे काम करता है, तो आप कोशिश कर सकते हैं और यह उम्मीद के मुताबिक काम करेगा। लेकिन क्या आप माणिक की भाषा को समझने की कोशिश नहीं कर रहे हैं और एक दुभाषिया का निर्माण कर रहे हैं या रूबी सीखते हुए रेल्स कोड को पढ़ रहे हैं। गो सरल है। आपको सलाह दी जाती है, दौरे के बाद, भाषा कल्पना पढ़ने के लिए - यहां तक ​​कि शुरुआती भी कर सकते हैं। और वे सबसे उन्नत कोड पढ़ सकते हैं और प्राप्त कर सकते हैं। क्योंकि यह सरल है।
किक

4
@kik इसका कोई मतलब नहीं है। सरल का मतलब फीचरलेस नहीं है। सॉर्ट सबसे महत्वपूर्ण और मौलिक में से एक है, फिर भी एक पुस्तकालय में सरल विशेषताएं हो सकती हैं। गोलांग में html टेम्प्लेट, crc32 हैश, प्रिंटर, स्कैनर आदि के लिए एक मानक पुस्तकालय है, जो इसे NO LESS सरल बनाता है। आपके मानक पुस्तकालय में कोई छँटाई नहीं करना सरलता का विषय नहीं है, यह एक बुनियादी बात है कि सभी भाषाओं को एक मानक माना जाता है। यहां तक ​​कि सी में एक छँटाई समारोह है। गोलंग के साथ इतना अभिजात्य होना बंद करो और यह विचार करना शुरू कर दो कि गोलंग इस पर गलत था (यदि यह वास्तव में ऐसा नहीं होता)।
21

318

जाओ 1.8 के रूप में आप अब उपयोग कर सकते हैं sort.Slice एक टुकड़ा सॉर्ट करने के लिए:

sort.Slice(planets, func(i, j int) bool {
  return planets[i].Axis < planets[j].Axis
})

आमतौर पर एक स्लाइस के बजाय एक सरणी का उपयोग करने का कोई कारण नहीं है, लेकिन आपके उदाहरण में आप एक ऐरे का उपयोग कर रहे हैं , इसलिए आपको [:]इसे काम करने के लिए स्लाइस (ऐड ) के साथ ओवरले करना होगा sort.Slice:

sort.Slice(planets[:], func(i, j int) bool {
  return planets[i].Axis < planets[j].Axis
})

सॉर्टिंग सरणी को बदल देता है, इसलिए यदि आप वास्तव में चाहते हैं कि आप सॉर्ट करने के बाद स्लाइस के बजाय सरणी का उपयोग करना जारी रख सकते हैं।


sort.Sliceआश्चर्य की बात है। lessसमारोह केवल सूचकांक लेता है तो यह करने के लिए (इस जवाब में) एक अलग से कब्जा कर लिया का उपयोग है planetsसरणी। ऐसा लगता है कि छंटे हुए स्लाइस और lessफ़ंक्शन समान डेटा पर काम कर रहे हैं। इस काम के लिए, आपको planetsतीन बार (DRY) टाइप करना होगा ।
ब्रेंट बर्नबर्न

planets[:]अत्यंत महत्वपूर्ण है। लेकिन मैं नहीं समझता कि क्यों। हालांकि काम करता है।
इस्पात

@STEEL आमतौर पर आपको पहली जगह में एक सरणी के बजाय एक स्लाइस का उपयोग करना चाहिए। फिर आपको जरूरत नहीं है [:]
आंद्रेकेआर

37

गो 1.8 के रूप में, @ आंद्रेकेआर का जवाब बेहतर समाधान है।


आप एक संग्रह प्रकार लागू कर सकते हैं जो सॉर्ट इंटरफ़ेस को लागू करता है

यहाँ दो ऐसे प्रकारों का उदाहरण दिया गया है जो आपको एक्सिस या नाम से छाँटने की अनुमति देते हैं:

package main

import "log"
import "sort"

// AxisSorter sorts planets by axis.
type AxisSorter []Planet

func (a AxisSorter) Len() int           { return len(a) }
func (a AxisSorter) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a AxisSorter) Less(i, j int) bool { return a[i].Axis < a[j].Axis }

// NameSorter sorts planets by name.
type NameSorter []Planet

func (a NameSorter) Len() int           { return len(a) }
func (a NameSorter) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a NameSorter) Less(i, j int) bool { return a[i].Name < a[j].Name }

type Planet struct {
    Name       string  `json:"name"`
    Aphelion   float64 `json:"aphelion"`   // in million km
    Perihelion float64 `json:"perihelion"` // in million km
    Axis       int64   `json:"Axis"`       // in km
    Radius     float64 `json:"radius"`
}

func main() {
    var mars Planet
    mars.Name = "Mars"
    mars.Aphelion = 249.2
    mars.Perihelion = 206.7
    mars.Axis = 227939100
    mars.Radius = 3389.5

    var earth Planet
    earth.Name = "Earth"
    earth.Aphelion = 151.930
    earth.Perihelion = 147.095
    earth.Axis = 149598261
    earth.Radius = 6371.0

    var venus Planet
    venus.Name = "Venus"
    venus.Aphelion = 108.939
    venus.Perihelion = 107.477
    venus.Axis = 108208000
    venus.Radius = 6051.8

    planets := []Planet{mars, venus, earth}
    log.Println("unsorted:", planets)

    sort.Sort(AxisSorter(planets))
    log.Println("by axis:", planets)

    sort.Sort(NameSorter(planets))
    log.Println("by name:", planets)
}

यह वास्तव में क्रिया समाधान है जो मैंने जोड़ा है, है ना?
मार्टिन थोमा

1
मैंने इसे लिखते समय इसे लिंक किया था। मैं क्षमाप्रार्थी हूं। लेकिन सिर्फ मानक टूल का उपयोग करने से, ऐसा करने का कोई तरीका नहीं है।
jimt

5

आप उस Sort interfaceपर लागू करने के बजाय, []Planetउस प्रकार पर लागू कर सकते हैं जिसमें संग्रह होता है और एक बंद होता है जो तुलना करेगा। आपको प्रत्येक संपत्ति के लिए तुलनात्मक समापन के लिए कार्यान्वयन प्रदान करना होगा।

यह तरीका मुझे लगता है कि संरचना के प्रत्येक गुण के लिए एक क्रमबद्ध प्रकार को लागू करने से बेहतर है।

यह उत्तर सॉर्ट डॉक्स से लगभग सही है, इसलिए मैं इसके लिए ज्यादा क्रेडिट नहीं ले सकता

package main

import (
    "log"
    "sort"
)

type Planet struct {
    Name       string  `json:"name"`
    Aphelion   float64 `json:"aphelion"`   // in million km
    Perihelion float64 `json:"perihelion"` // in million km
    Axis       int64   `json:"Axis"`       // in km
    Radius     float64 `json:"radius"`
}

type By func(p1, p2 *Planet) bool

func (by By) Sort(planets []Planet) {
    ps := &planetSorter{
        planets: planets,
        by:      by, 
    }
    sort.Sort(ps)
}

type planetSorter struct {
    planets []Planet
    by      func(p1, p2 *Planet) bool 
}

func (s *planetSorter) Len() int {
    return len(s.planets)
}

func (s *planetSorter) Swap(i, j int) {
    s.planets[i], s.planets[j] = s.planets[j], s.planets[i]
}

func (s *planetSorter) Less(i, j int) bool {
    return s.by(&s.planets[i], &s.planets[j])
}

इसे कैसे बुलाएंगे।

func main() {
    /* Same code as in the question */

    planets := []Planet{*mars, *venus, *earth}

    By(func(p1, p2 *Planet) bool {
        return p1.Name < p2.Name
    }).Sort(planets)

    log.Println(planets)

    By(func(p1, p2 *Planet) bool {
        return p1.Axis < p2.Axis
    }).Sort(planets)

    log.Println(planets)
}

यहाँ एक डेमो है


3

बॉयलर प्लेट में से कुछ को कम करने का एक और तरीका यहां दिया गया है। अस्वीकरण, यह प्रतिबिंब और नुकसान प्रकार की सुरक्षा का उपयोग करता है।

यहाँ एक डेमो है

सभी जादू Propसमारोह में होता है । यह सॉर्ट करने के लिए स्ट्रक्चर प्रॉपर्टी लेता है और इसे जिस क्रम में आप सॉर्ट करना चाहते हैं (आरोही, अवरोही) और एक फ़ंक्शन देता है जो तुलना करेगा।

package main

import (
    "log"
    "reflect"
    "sort"
)

func test(planets []Planet) {
    log.Println("Sort Name")
    By(Prop("Name", true)).Sort(planets)
    log.Println(planets)

    log.Println("Sort Aphelion")
    By(Prop("Aphelion", true)).Sort(planets)
    log.Println(planets)

    log.Println("Sort Perihelion")
    By(Prop("Perihelion", true)).Sort(planets)
    log.Println(planets)

    log.Println("Sort Axis")
    By(Prop("Axis", true)).Sort(planets)
    log.Println(planets)

    log.Println("Sort Radius")
    By(Prop("Radius", true)).Sort(planets)
    log.Println(planets)
}

func Prop(field string, asc bool) func(p1, p2 *Planet) bool {
    return func(p1, p2 *Planet) bool {

        v1 := reflect.Indirect(reflect.ValueOf(p1)).FieldByName(field)
        v2 := reflect.Indirect(reflect.ValueOf(p2)).FieldByName(field)

        ret := false

        switch v1.Kind() {
        case reflect.Int64:
            ret = int64(v1.Int()) < int64(v2.Int())
        case reflect.Float64:
            ret = float64(v1.Float()) < float64(v2.Float())
        case reflect.String:
            ret = string(v1.String()) < string(v2.String())
        }

        if asc {
            return ret
        }
        return !ret
    }
}

type Planet struct {
    Name       string  `json:"name"`
    Aphelion   float64 `json:"aphelion"`   // in million km
    Perihelion float64 `json:"perihelion"` // in million km
    Axis       int64   `json:"Axis"`       // in km
    Radius     float64 `json:"radius"`
}

type By func(p1, p2 *Planet) bool

func (by By) Sort(planets []Planet) {
    ps := &planetSorter{
        planets: planets,
        by:      by, // The Sort method's receiver is the function (closure) that defines the sort order.
    }
    sort.Sort(ps)
}

type planetSorter struct {
    planets []Planet
    by      func(p1, p2 *Planet) bool // Closure used in the Less method.
}

// Len is part of sort.Interface.
func (s *planetSorter) Len() int { return len(s.planets) }

// Swap is part of sort.Interface.
func (s *planetSorter) Swap(i, j int) {
    s.planets[i], s.planets[j] = s.planets[j], s.planets[i]
}

// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
func (s *planetSorter) Less(i, j int) bool {
    return s.by(&s.planets[i], &s.planets[j])
}

func main() {
    test(dataSet())
}

func dataSet() []Planet {

    var mars = new(Planet)
    mars.Name = "Mars"
    mars.Aphelion = 249.2
    mars.Perihelion = 206.7
    mars.Axis = 227939100
    mars.Radius = 3389.5

    var earth = new(Planet)
    earth.Name = "Earth"
    earth.Aphelion = 151.930
    earth.Perihelion = 147.095
    earth.Axis = 149598261
    earth.Radius = 6371.0

    var venus = new(Planet)
    venus.Name = "Venus"
    venus.Aphelion = 108.939
    venus.Perihelion = 107.477
    venus.Axis = 108208000
    venus.Radius = 6051.8

    return []Planet{*mars, *venus, *earth}
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.