Riordinare un elenco principale basato su un sottoinsieme riordinato


19

Di recente ho avuto un problema da risolvere sul lavoro in cui avevo due elenchi: un elenco principale e un elenco più piccolo che contiene un sottoinsieme degli elementi nell'elenco principale potenzialmente in un ordine diverso. Avevo bisogno di riordinare l'elenco principale in modo tale che gli articoli nel sottoinsieme apparissero nello stesso ordine senza cambiare l'ordine degli articoli non presenti nell'elenco e mantenere gli articoli nella stessa posizione ogni volta che fosse possibile. Va bene, probabilmente suona confuso, quindi lo analizzerò:

  • L'elenco principale definisce l'ordine predefinito degli articoli.
  • L'elenco dei sottoinsiemi definisce l'ordine relativo di alcuni elementi.
  • Laddove l'elenco principale abbia due elementi fuori servizio in base all'elenco dei sottoinsiemi, l'elemento precedente nell'elenco principale dovrebbe essere spostato nel primo indice in cui si trova nella posizione corretta rispetto ad altri elementi nell'elenco dei sottoinsiemi. (cioè immediatamente dopo l'elemento successivo)

Il tuo compito è implementare questo algoritmo di riordino.

Esempi di casi di test

Master: [1, 2, 3]
Subset: []
Result: [1, 2, 3]

Master: [9001, 42, 69, 1337, 420]
Subset: [69]
Result: [9001, 42, 69, 1337, 420]

Master: [9001, 42, 69, 1337, 420, 99, 255]
Subset: [69, 9001, 1337]
Result: [42, 69, 9001, 1337, 420, 99, 255]

Master: [1, 2, 3, 4, 5]
Subset: [2, 5]
Result: [1, 2, 3, 4, 5]

Master: [apple, banana, carrot, duck, elephant]
Subset: [duck, apple]
Result: [banana, carrot, duck, apple, elephant]

Master: [Alice, Betty, Carol, Debbie, Elaine, Felicia, Georgia, Helen, Ilene, Julia]
Subset: [Betty, Felicia, Carol, Julia]
Result: [Alice, Betty, Debbie, Elaine, Felicia, Carol, Georgia, Helen, Ilene, Julia]

Master: [snake, lizard, frog, werewolf, vulture, dog, human]
Subset: [snake, werewolf, lizard, human, dog]
Result: [snake, frog, werewolf, lizard, vulture, human, dog]

Master: [Pete, Rob, Jeff, Stan, Chris, Doug, Reggie, Paul, Alex]
Subset: [Jeff, Stan, Pete, Paul]
Result: [Rob, Jeff, Stan, Pete, Chris, Doug, Reggie, Paul, Alex]

Master: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
Subset: [8, 1, 2, 12, 11, 10]
Result: [3, 4, 5, 6, 7, 8, 1, 2, 9, 12, 11, 10]

Master: [lol, rofl, lmao, roflmao, lqtm, smh, jk, wat]
Subset: [wat, lmao, rofl]
Result: [lol, roflmao, lqtm, smh, jk, wat, lmao, rofl]

Regole

  • Scappatoie standard, yadda yadda, I / O conveniente, blah blah.
  • Anche se gli esempi usano numeri e stringhe, devi solo supportare un tipo di elemento, che sia numeri interi, stringhe o qualsiasi altra cosa con semantica di uguaglianza ben definita, compresi elenchi eterogenei se è conveniente nella tua lingua.
  • Si può presumere che sia l'elenco principale che l'elenco dei sottoinsiemi non contengano duplicati
  • Si può presumere che tutti gli elementi trovati nell'elenco dei sottoinsiemi siano presenti nell'elenco principale
  • Entrambi gli elenchi potrebbero essere vuoti
  • È necessario supportare almeno matrici lunghe fino a 100 elementi.
  • Il riordino può essere implementato sul posto o attraverso la creazione di un nuovo elenco / array.

Buon golf!


1
Un bel problema robusto.
Giona

È 8 1 3 4 5 6 7 2 9 12 11 10una soluzione valida per la penultima?
Ven

@Ven No. Anche se si adatta ai vincoli di mantenere gli articoli del sottoinsieme nello stesso ordine relativo, volevo assicurarmi che ci fosse una sola risposta corretta, quindi l'articolo precedente fuori servizio dovrebbe essere spostato dopo più tardi articolo fuori servizio.
Beefster

Perché è importante che ci sia più di una risposta corretta? Si prega di aggiungere il vincolo alle regole della sfida per favore.
Ven

Risposte:


4

Retina 0.8.2 , 51 byte

+`(\b(\w+),(\w+)\b.*¶.*\b)\3,(.*\b\2\b)
$1$4,$3
1A`

Provalo online! Accetta l'input come un elenco di parole chiave separate da virgola nella prima riga e un elenco principale di parole separate da virgola nella seconda riga. Spiegazione:

(\b(\w+),(\w+)\b.*¶.*\b)\3,(.*\b\2\b)

Trova due parole chiave adiacenti in cui la seconda parola precede la prima nell'elenco principale.

$1$4,$3

Sposta la seconda parola in modo che appaia dopo la prima parola nell'elenco principale.

+`

Ripeti fino a quando non vengono visualizzate parole non funzionanti.

1A`

Elimina le parole d'ordine.


4

JavaScript (ES6),  96 89 74  71 byte

Questo è iniziato come un casino ingombrante e alla fine si è ridotto a una forma piuttosto concisa ed elegante. Vorrei ringraziare il metodo .splice () per la sua fruttuosa collaborazione su quello. ;)

Accetta input come (master)(subset). Emette aggiornando l'elenco principale.

m=>s=>s.map(p=x=>m.splice(p,0,...m.splice(i=m.indexOf(x),p>i||!(p=i))))

Provalo online!

Come?

ip

m.splice(p, 0, ...m.splice(i, condition))

1

  • i[element]
  • p

0

  • il .splice () interno non rimuove nulla e restituisce un array vuoto
  • di conseguenza, il .splice () esterno riceve non definito come terzo argomento e non viene inserito nulla

Commentate

m => s =>                 // m[] = master list, s[] = subset list
  s.map(                  //
    p =                   // p = position in the master list of the last element from
                          //     the subset list (initialized to a non-numeric value)
    x =>                  // for each element x in the subset list:
    m.splice(             //   insert in the master list:
      p,                  //     at position p
      0,                  //     without removing any element
      ...m.splice(        //     remove from the master list and flatten:
        i = m.indexOf(x), //       i = position of x in the master list
        p > i             //       if p is greater than i, remove x from its current
                          //       position and insert it at position p
        || !(p = i)       //       otherwise, set p to i and don't remove/insert anything
      )                   //     end of inner splice()
    )                     //   end of outer splice()
  )                       // end of map()

1
"Vorrei ringraziare il metodo .splice () per ..." Cue PPCG Oscar's Music ... :)
Chas Brown,

Più correttamente, la chiamata di splice esterna riceve 3 o 2 argomenti rispettivamente, il che lo fa fare la cosa giusta.
Neil

2

Haskell, 79 byte

(m:n)#u@(s:t)|m==s=m:n#t|all(/=m)u=m:n#u|(x,_:z)<-span(/=s)n=(x++s:m:z)#u
m#_=m

Provalo online!

(m:n)#u@(s:t)                 -- m: head of master list
                              -- n: tail of master list
                              -- s: head of subset
                              -- t: tail of subset
                              -- u: whole subset
   |m==s                      -- if m==s
        =m:n#t                -- return 'm' and append a recursive call with 'n' and 't'
   |all(/=m)u                 -- if 'm' is not in 'u'
             =m:n#u           -- return 'm' and append a recursive call with 'n' and 'u'
   |                          -- else (note: 's' is element of 'n')
    (x,_:z)<-span(/=s)n       -- split 'n' into a list 'x' before element 's' and
                              -- a list 'z' after element 's' and
       = (x++s:m:z)#u         -- make a recursive call with
                              -- x++s:m:z as the new master list (i.e. 'm' inserted into 'n' after 's') 
                              -- and 'u'
m # _ = m                     -- if either list is emtpy, return the master list

2

Rubino , 73 68 byte

->a,b{0while b.zip(a&b).find{|m,n|m!=n&&a=a[0..a.index(m)]-[n]|a};a}

Provalo online!

Come?

  • L'intersezione tra ae bcontiene tutti gli elementi di b, ma nello stesso ordine in cui li troveremmoa
  • Quindi, se ripetiamo su be sull'intersezione in parallelo, non appena troviamo una differenza, possiamo spostare un singolo elemento.
  • La delocalizzazione viene eseguita tagliando ala posizione dell'elemento che abbiamo trovato b, quindi rimuovendo l'elemento che abbiamo trovato nell'intersezione e quindi aggiungendo il resto di a.
  • Ripeti dall'inizio, fino a quando tutti gli elementi di bsono nell'ordine giusto ina

cosa sta facendo lo 0 0while?
Giona

È solo un NOP.
GB

perché è necessario?
Giona

1
Poiché il confronto e la manipolazione vengono eseguiti in un singolo blocco, per evitare di dichiarare una variabile prima di avviare il ciclo. Significa: "non fare nulla mentre l'operazione restituisce true.", Il codice è più corto di "esegui operazione mentre il risultato è true"
GB


1

Perl 6 , 40 byte

{*.permutations.first(*.grep(.any)eq$_)}

Provalo online!

Blocco di codice anonimo che accetta l'input curry (come f(subList)(masterList)e trova la prima permutazione lessicale degli indici dell'elenco principale in cui gli elementi dell'elenco secondario sono nell'ordine corretto.

Intuitivamente, la prima permutazione soddisfacente lascerà gli elementi correttamente ordinati nell'ordine originale, spostando in avanti quelli posizionati in modo errato alla distanza minima necessaria per averli nell'ordine corretto, che li posiziona direttamente dopo l'elemento precedente nel sottoinsieme.

Spiegazione:

{*                                     } # Anonymous code block that returns a lambda
  .permutations                          # In all permutations of the master list
               .first(                )  # Find the first permutation
                     (*.grep(.any)       # Where the order of the subset
                                  eq$_   # Is the same as the given order


1

Gelatina , 9 byte

Œ!iⱮṢƑ¥ƇḢ

Provalo online! o suite di test

Inefficiente, in particolare con elenchi master di grandi dimensioni. Genera tutte le possibili permutazioni, filtra quelle in cui il sottoinsieme è nell'ordine sbagliato e quindi restituisce il primo.

Spiegazione

Œ!        | Generate all permutations of the master list
      ¥Ƈ  | Filter including only those where...
  iⱮ      |   the index of each sublist item in this permutation...
     Ƒ    |   is...
    Ṣ     |   in order. 
        Ḣ | Finally take the first item

Ciò non sembra conforme alla regola "Laddove l'elenco principale abbia due elementi non ordinati in base all'elenco dei sottoinsiemi, l'elemento precedente nell'elenco principale dovrebbe essere spostato nell'indice precedente in cui si trova nella posizione corretta rispetto ad altri elementi all'interno dell'elenco dei sottoinsiemi (ovvero immediatamente dopo l'elemento successivo) "
Beefster

@Beefster funziona su quelli che ho provato finora. Penso che l'ordinamento delle permutazioni sia tale che questo sia il risultato corretto. Felice di essere smentito se c'è un controesempio.
Nick Kennedy,

@Beefster Ora ho provato tutti i tuoi esempi tranne i nomi femminili e l'1..12 e l'ordinamento del risultato è corretto.
Nick Kennedy,

2
@Beefster La mia risposta ha una spiegazione parziale del perché funziona
Jo King,

1

J , 49 byte

[:(<@({:+i.@>:@-/)@i.~C.])^:(>/@i.~)&.>/]|.@;2<\[

Provalo online!

spiegazione

Prendiamo il sottoinsieme come argomento sinistro e l'input completo come diritto.

Lavoreremo attraverso il codice con un esempio specifico per chiarezza:

5 2 4 f 1 2 3 4 5

Prendi gli infissi in scatola della dimensione due del sottoinsieme:

2 <\ [

produzione:

┌───┬───┐
│5 2│2 4│
└───┴───┘

aggiungerli all'input originale e invertire il tutto:

] |.@;

Noi abbiamo:

┌───┬───┬─────────┐
│2 4│5 2│1 2 3 4 5│
└───┴───┴─────────┘

Risolvere il problema diventa una riduzione da destra a sinistra su quanto sopra. Dobbiamo solo trovare il verbo giusto da inserire /tra gli elementi.

Ogni iterazione della riduzione aggiornerà la casella più a destra (l'input completo, che stiamo trasformando) in modo che sia conforme al vincolo di ordinamento rappresentato dalla coppia alla sua sinistra. Al termine della riduzione, l'ingresso rispetterà l'ordine completo del sottoinsieme.

Se l'ordinamento della coppia è uguale all'ordinamento nell'input, quanto segue valuterà 0 e non faremo nulla:

^:(>/@i.~)

Altrimenti valuterà 1 e applicheremo il verbo a sinistra di ^:

   {: + i.@>:@-/)@i.~ C. ]

che sposta l'elemento a sinistra a destra dell'elemento a destra. Questo movimento è semplicemente una permutazione ciclica di tutti gli elementi tra (e inclusi) i due elementi in questione.

J ha primitivo per applicare una tale permutazione ciclica:

<cyclic permutation definition> C. ]

e il resto del verbo non fa altro che individuare gli indici di cui abbiamo bisogno per scorrere:

{: + i.@>:@-/)@i.~

che sembra più lungo di quanto dovrebbe essere, ma non sono stato in grado di giocare ulteriormente a quella frase.

Alla fine riordiniamo il risultato <@e abbiamo finito.


0

Gelatina , 24 byte

i@€MƤFṬœṗƲḊ;JḟF}W€ʋ@¥ṢFị

Provalo online! o suite di test

Spiegazione

Un collegamento diadico che accetta il sottoinsieme come elenco di sinistra e principale come argomenti di destra. L'esempio seguente utilizza 9001, 42, 69, 1337, 420, 99, 255 come master e 69, 9001, 1337 come sottoinsieme.

i@€                      | Find the index of each subset item in the master list [3, 1, 4]
         Ʋ               | Previous 4 links as a monad
   MƤ                    | Find the index of the maximum for each prefix of this list [1, 1, 3]
     F                   | Flatten (because the previous result are actually each length one lists)
      Ṭ                  | Convert to a boolean list [1,0,1]
       œṗ                | Partition the [3, 1, 4] list before each 1 [[], [3, 1], [4]]
          Ḋ              | Remove the empty first list [[3, 1], [4]]
                    ¥    | Previous two links as a dyad
                  ʋ@     | Previous 4 links as a dyad with reversed arguments
            J            | Sequence along the master list [1, 2, 3, 4, 5, 6, 7]
             ḟF}         | Filter out items in the flattened [3, 1, 4] list
                W€       | Wrap each item as a list [[2], [5], [6], [7]]
           ;             | Concatenate rhis to the [[3, 1], [4]] list
                     Ṣ   | Sort (effectively by first item in each list) [[2], [3, 1], [4], [5], [6], [7]]
                      F  | Flatten
                       ị | Look up in original master list (and implicitly output)

0

C # (compilatore interattivo Visual C #) , 118 byte

a=>b=>{for(int j;b.Any();)foreach(var e in b.Intersect(a.Take(j=a.IndexOf(b.Dequeue())))){a.Remove(e);a.Insert(j,e);}}

Provalo online!

Sfruttando alcune classi nello System.Collections.Genericspazio dei nomi. Il master è a List<T>e il sottoinsieme è a Queue<T>.

// a: master
// b: subset
a=>b=>{
  // continue until b is empty
  for(int j;b.Any();)
    // iterate over values that are out of order in a
    // per the head of b using loop variable e
    foreach(var e in
      // the out of order values are determined by
      // intersecting remaining values in b with
      b.Intersect(
        // values in a occurring before the current head of b
        // save the position in a to variable j and remove the head of b
        a.Take(j=a.IndexOf(b.Dequeue()))
      )
    ){
      // push back the out of order element in a
      a.Remove(e);
      a.Insert(j,e);
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.