Gluttonous Colluding Numbers

Here's an easy one, with just enough complexity to make golfing non-trivial.

Input

1. A list of non-negative numbers representing people waiting in line for Thanksgiving dessert. The first (leftmost) number is first in line, and the list can contain repeats (think of each number as a person's first name):
Note: Pie not part of actual input.

(
)
__..---..__
,-='  /  |  \  =-.
:--..___________..--;   6 4 3 1 2 3 0
\.,_____________,./


1. A non-empty subset of that list, representing a group of colluding friends who will "chat and cut" to skip ahead in line. All colluders will move just behind the friend already closest to the front of the line, but otherwise retain their relative positions. Everyone they skip past will also retain their relative position.

Output

The new list, after a successful (possibly multi-way) chat and cut. For example, in the list above, for a friend group of 0 2 4, the output would be:

6 4 2 0 3 1 3


If the friend group was 4 3, the output would be:

6 4 3 3 1 2 0


Notes

This is code golf with standard site rules.

• A single colluder x in the colluder list makes everyone in line with that number a colluder.
• The list of people waiting may contain repeats.
• The colluder list will not contain repeats.
• The colluder list may not be sorted.
• The colluder list will have between 2 and U items, where U is the number of unique elements in the queuer list.

Test Cases

The format is:

1. People in line.
2. List of colluders.
3. Output.
5 4 3 2 1 0
3 1
5 4 3 1 2 0

1 2 1 3 1 9
1 9
1 1 1 9 2 3

1 2 3 4 5
4 5
1 2 3 4 5

7 6 1 5 2 4 3
4 5 6
7 6 5 4 1 2 3

7 6 1 5 2 4 3
1 2 3
7 6 1 2 3 5 4

7 6 1 5 2 4 3
3 6
7 6 3 1 5 2 4

1 2 3 4
1 2 3 4
1 2 3 4

1 2 3 4 5 4
5 4
1 2 3 4 5 4

• Suggested test case: 1 2 3 4 5 4, 5 4 -> 1 2 3 4 5 4, because it has duplicate colluders on either side of a different colluder. – Unrelated String Nov 24 '20 at 17:21
• @UnrelatedString Added. – Jonah Nov 24 '20 at 17:31
• Suggest having a testcase with some multi-digit numbers. – Noodle9 Nov 29 '20 at 16:10

05AB1E, 9 8 bytes

ΣåDi©}®‹


-1 byte thanks to @ovs.

Explanation:

Σ        # (Stable) sort the first (implicit) input-list by:
å       #  Check that the current item is in the second (implicit) input-list
#  (1 if truthy; 0 if falsey)
Di }   #  Duplicate it, pop this copy, and if it's 1:
©    #   Store 1 in variable ® (without popping) (variable ® is -1 by default)
®‹ #  Check that the duplicated check is smaller than ®
#  (1 if truthy; 0 if falsey)


Here the same TIOs with map instead of sort-by, so you can see any leading non-friends become 0, all friends become 0 as well, and any non-friends after the first friend become 1:
Try it online or verify all test cases.

• Nice use of the default register value! You can save 1 byte with ≠®+ -> ®‹, this maps leading non-friends and colluders to 0 and other non-friends to 1. – ovs Nov 24 '20 at 10:24
• @ovs Nice! I had the feeling it could be golfed somehow, but didn't think of using a comparison. I was fiddling around with plus/minus and/or doing the if-statement at the end to get rid of the }, but that only resulted in some alternatives with the same 9-bytes byte-count. – Kevin Cruijssen Nov 24 '20 at 10:34

J, 12 bytes

-1 thanks to FrownyFrog!

[/:OR\@e.-e.


Try it online!

For 6 4 3 1 2 3 0 and 0 2 4:

[/:OR\@e.-e.
OR        OR …
\       each prefix of …
@e.    the bitmask of friends, or:
OR\@e.    after the first friend? (0 1 1 1 1 1 1)
e. the bitmask of friends  (0 1 0 0 1 0 1)
-   minus                   (0 0 1 1 0 1 0)
[/:          (stable) sort the people based on the list
(6 4 3 1 2 3 0)

• This is excellent J – Jonah Nov 23 '20 at 23:17
• >./ can be OR for −1 – FrownyFrog Nov 24 '20 at 4:22
• @FrownyFrog Nice, TIL. – Jonah Nov 24 '20 at 17:32

R, 494745 42 bytes

Edit: -2 bytes, and then -3 more bytes, thanks to Giuseppe

function(l,f)l[order(cummax(p<-l%in%f)-p)]


Try it online!

Commented:

function(l,f           # function with arguments:
# l = line
# f = friends
p=l%in%f)               # define p as TRUE at positions of friends in the line
l[order                 # now output l, in the order of (lowest>highest):
-(                      # minus (so now highest>lowest)
p                       # 1 for each friend
+2*!cumsum(p)           # +2 for anyone ahead of the first friend
))]                     # (so, in the end, anyone ahead of all friends gets -2,
# the friends get -1,
# and anyone else behind the first friend gets zero).


Previous approach: R, 67 59 54 bytes

Edit: -8 bytes thanks to Giuseppe, then -5 more bytes thanks to Robin Ryder

function(l,f,p=which(l%in%f))c(l[z<-c(1:p-1,p)],l[-z])


Try it online!

• Nice! Thanks! I was trying to get rid of the [1], but stuck with seq. This is much better! – Dominic van Essen Nov 23 '20 at 22:07
• 54 bytes by including p in the definition of z. – Robin Ryder Nov 23 '20 at 22:45
• – Giuseppe Nov 24 '20 at 15:10
• @Giuseppe Yes - of course (note: everything is 'of course' in retrospect...)! Thanks! – Dominic van Essen Nov 24 '20 at 16:20
• Ah, 42 bytes -- I cheated and looked at the Jelly and J answers for inspiration though (I forgot that cummax existed!) – Giuseppe Nov 24 '20 at 16:33

Python 2, 79 72 bytes

def f(x,y):m=min(map(x.index,y));print x[:m]+sorted(x[m:],cmp,y.count,1)


-7 bytes thanks to @ovs

I take a unique approach here using Python's sorted function, which performs a stable sort. Might not be the shortest Python approach, but it is interesting.

def f(x,y):  # x = input line, y = list of colluders
m = min(map(x.index,y)) # index of the first colluder in line
print
x[:m] # The first m people do not change positions
sorted(
x[m:],   # we sort the people after the first m
cmp,    # default comparison function
y.count, # key function is y.count: returns 0 for numbers not in y
# and 1 for numbers in y
1        # sort in descending order
)

• You can use the builtin cmp instead of None as the second argument of sorted. And min(map(x.index,y)) saves a few more bytes. – ovs Nov 23 '20 at 22:25

(h:t)%c|all(/=h)c=h:t%c
l%c=[x|b<-[1<0..],x<-l,elem x c/=b]


Try it online!

Test cases and inspiration from ovs's solution.

We use [1<0..] to generate the list [False,True]. Having handled the prefix of non-colluding numbers, we then pass over the remainder twice, first to take colluding numbers, then to take non-colluding ones. A same-length alternative is:

59 bytes

(h:t)%c|all(/=h)c=h:t%c
l%c=[x|b<-[id,not],x<-l,b$elem x c]  Try it online! Jelly, 9 bytes e€»\_$Ụị⁸


Try it online!

More or less a translation of xash's J answer.

e€           For each person in line, are they a friend?
_        Subtract that from
»\ $its cumulative maxima, Ụ sort the indices, ị⁸ index back into the line.  iⱮ»\_$Ụị almost saves a byte by taking colluders on the left and the line on the right, but it doesn't succeed at preserving the order of duplicate colluders in cases such as [1,2,3,4,5,4], [5,4].

Jelly, 12 bytes

iⱮṂœṖ⁸e¬¥Þ€F


Try it online!

• e¬¥ feels not good – Unrelated String Nov 24 '20 at 0:43

Factor, 62 bytes

[ '[ _ member? ] 2dup find . swapd cut rot partition 3append ]


Try it online!

The extensive sequences library is quite helpful here. Takes line cheaters on the stack, and returns the modified line through the stack. Prints a garbage to stdout each time it is run.

Ungolfed

[                  ! ( line cheaters ) start anonymous lambda definition
'[ _ member? ]   ! ( line fun ) convert cheaters array to membership function
2dup find .      ! ( line fun idx ) find index of first cheater
swapd cut        ! ( fun left right ) cut line at that index
rot partition    ! ( left cheaters rest ) partition the right into
!   cheaters and non-cheaters, using the membership function
3append          ! ( ans ) concatenate the three arrays
]                  ! end anonymous lambda definition


Factor, 74 bytes

[ dupd '[ _ member? 1 0 ? ] map dup cum-max swap v- zip sort-values keys ]


Try it online!

Factor's standard library is feature-rich enough that it can almost directly replicate xash's J solution. Except that it's 5~10 times bloated byte-count-wise.

Ungolfed

: glutton ( line cheaters -- line' )  ! takes two arrays on the stack
! and returns the cheated line as an array
dupd                       ! duplicate line under cheaters
'[ _ member? 1 0 ? ] map   ! for each number in line, 1 if it is a cheater,
! 0 otherwise
dup cum-max swap v-        ! cumulative maximum minus self (of the above)
zip sort-values keys       ! sort line by the above
;

• Nice solutions! – Galen Ivanov Nov 24 '20 at 11:52
• Out of curiosity, are you playing with Factor just for golfing, or doing real work in it? How do you like it? – Jonah Nov 24 '20 at 17:38
• @Jonah I just picked it up for fun. It's not quite like programming in traditional stack-based languages; it feels like a mixture of Haskell and Jelly with <insert a stack language here> when written in idiomatic style. (Except that, as always, non-idiomatic style often wins in code golf.) – Bubbler Nov 24 '20 at 22:57

q!c|f<-(elemc),(h,t)<-span(not.f)q=h++((filtert)=<<[f,not.f])


Try it online!

Commented:

q!c|
f<-(elemc),        -- f is a function to check whether a number is a colluder
(h,t)<-span(not.f)q  -- h is the longest prefix of non-colluders, t the remaining list
=
h++                  -- the result is h and ...
(  =<<               -- ... the concatenation of the results of ...
(filtert)         -- ... filtering the remaining list by ...
[f,not.f])      -- ... f and not f (colluders and not colluders in t)


With some inspiration from xnor's and AZTECCO's answers I came up with a pointfree function that is just 1 byte longer.

(takeWhile<>g.(not.)<>((.).g<*>dropWhile)).flip(all.(/=))
g=filter


Try it online! In GHC version 8.4.1 and above (<>) is included in the Prelude, in the old version on TIO it has to be imported manually.

Racket, 145 bytes

(λ(a b)(let([m(apply min(map(λ(x)(index-of a x))b))])(append(take
a m)(call-with-values(λ()(partition(λ(x)(member x b))(drop a m)))append))))


Try it online!

That's even longer than my Red solution :)

It's insane that partition returns two separate values rather than a list with two sublists - that's why I need to use call-with-values and put partition within an additional lambda...

Ruby 2.7, 39 bytes

->q,c{q.flat_map{c-[_1]!=c ?q-q-=c:_1}}


Try it online! (40 bytes because TIO's Ruby version doesn't support numbered block parameters)

Relies on the Array::- method, which returns all elements of the first array not present in the second. Given a queue q and colluders c, the ordered lists of non-colluders and colluders are q-c and q-(q-c), respectively.

->q,c{
q.flat_map{ # For each person in the queue
c-[_1]!=c # Are they a colluder?
?q-q-=c # If they are the first colluder, replace them with all colluders (in same order as they appear in the queue) and remove remaining colluders from the queue in one fell swoop. Second and subsequent colluders will be replaced by an empty array.
:_1     # If not a colluder, leave them alone
}
}


K (oK), 20 bytes

{x@<{(|\x)-x}x in y}


Try it online!

An experiment in making a K port of xash's J answer - please upvote his/hers solution!

import Data.List.Split
q?c|h:t<-splitOneOf c q=id=<<h:filter(elemc)q:t


Try it online!

• Longer than @xnor and @ovs great answers but I think it's worth posting because it's quite short and it has a simple and interesting approach.

• Thanks to @ovs for the precious advices, saved a lot and looks more golfy now!

q?c ... =             - infix function taking q-ueue and c-olluders lists
|h:t<-splitOneOf c q  - split queue at colluders
id=<<                 - concat map :
h:filter(elemc)q:t    - first split, colluders and the remaining

• h++filter(elemc)q++concat t can be shortened to concat$h:filter(elemc)q:concat t which can in turn be shortened to id=<<h:filter(elemc)q:t. And there is splitOneOf, which saves another 5 bytes: tio.run/… – ovs Nov 24 '20 at 14:10 Perl 5, 75 bytes sub{$f=1;map$_%1e5,sort map$_+1e5*++$r+1e9*($_~~@_&&($f=2)?1:$f),@{+shift}}


Try it online!

sub{
$f=1; #f = 1 if first colluder not found, = 2 after 1st colluder map$_%1e5,      #remove part used for sort, input elems remain in sorted order
sort            #sorts numerically as all elems have same number of digits now
map             #converts each elem in input queue to sum of:
$_ #the elem itself +1e5 * ++$r    #plus 100000 times the input rank in the queue
+1e9 * (       #plus 1 billion times the "rank overriding number" which is:
$_~~@_&&($f=2) #true if current elem is colluder (if so, set $f=2) ? 1 #...1 if colluder :$f        #...or \$f if not colluder (f is 1 before 1st colluder, 2 after)
),
@{+shift}     #input queue
}


Works for input elems 0-99999 and for queues up to 10000 elems. Increase 1e5 and 1e9 to 1e8 and 1e16 accordingly (spending one more byte of code) to avoid this, after which computer memory size becomes a bottleneck for most setups I guess.

JavaScript (ES10), 60 bytes

Expects (people)(subset), where people is an array and subset is a Set.

a=>b=>a.flatMap(i=>b.has(i)?j=a.filter(x=>j&b.has(x)):i,j=1)


Try it online!

Or Try it with map() to get a better picture of the process.

How?

The variable j is initially set to 1.

We walk through the queue with flatMap(). Each identifier i that is not included in the group of friends is copied as-is:

b.has(i) ? … : i


As soon as a guy from the group of friends is reached, we append all friends at once in their order of appearance in the queue, and assign the result to j:

j = a.filter(x => j & b.has(x))


Arrays holding at least two elements are turned into NaN when coerced to a number. So the next time a friend is encountered, j & b.has(x) will be falsy(1) and filter() will return an empty array. As a result, all following friends are ignored as expected.

(1): It could actually be truthy if j is updated to a singleton array holding an odd identifier. But that would mean that there's only one friend in the queue, so the test b.has(i) would not be triggered another time in that case anyway.

C (gcc), 201 $$\\cdots\$$ 172 171 bytes

Saved 12 a whopping 29 30 bytes thanks to ceilingcat!!!

#define z 0;for(i=-1;++i<n;
#define P&&printf("%d ",l[i]
q;p;i;j;f(l,n,c,m)int*l,*c;{q=p=z!p P))for(j=m;j--;)l[i]==c[j]?q=p?q:i,l[i]^=p=-1:z)0>l[i]P^-1);z)i>=q&0<=l[i]P);}


Try it online!

Function takes a list of people waiting in line, its length, a list of colluding friends, and its length. Prints the new list to stdout.

Charcoal, 26 bytes

≔⁻θ⁻θηε≔⌕θ§ε⁰ζＩ…θζＩεＩ✂⁻θηζ


Try it online! Link is to verbose version of code. Explanation:

≔⁻θ⁻θηε


Get the sublist of the colluding friends. This is actually calculated by removing the entries of colluding friends from the original list, then removing those entries from the original list.

≔⌕θ§ε⁰ζ


Get the position of the first of the colluding friends in the original list.

Ｉ…θζ


Output everyone ahead of the first of the colluding friends.

Ｉε


Output the colluding friends.

Ｉ✂⁻θηζ


Output everyone else who wasn't ahead. (We already calculated this list but it actually costs a byte to save this rather than recalculating it.)

Red, 124 bytes

func[a b][m: 0 until[m: m + 1 find b a/:m]rejoin[take/part a m - 1
collect[foreach c a[if find b c[keep c]]]difference a b]]


Try it online!