Newer versions of Z3 no longer keep/print all function instances - z3

Notice in the new versions of Z3, the model only prints the arguments to the function that have a value other than the default. Is there a way to return to the old method of receiving all instances, including those that map to the default value?
Code:
import z3
config_init = z3.Function('config_init', z3.IntSort(), z3.IntSort(), z3.IntSort(), z3.IntSort())
def fooY(W, X, Y, Z):
return config_init(W, X, Y) == Z
s = z3.Solver()
s.add(fooY(1, 2, 3, 4))
s.add(fooY(2, 3, 4, 5))
s.add(fooY(1, 2, 8, 4))
s.add(fooY(2, 3, 9, 5))
print("Z3 Version", z3.get_version())
if s.check() == z3.sat:
mod = s.model()
print(mod)
else:
print('failed')
print(s.unsat_core())
Old Z3 Output
('Z3 Version', (4L, 5L, 1L, 0L))
[config_init = [(1, 2, 3) -> 4,
(2, 3, 4) -> 5,
(1, 2, 8) -> 4,
(2, 3, 9) -> 5,
else -> 4]]
New Z3 Output
Z3 Version (4, 8, 7, 0)
[config_init = [(2, 3, 4) -> 5, (2, 3, 9) -> 5, else -> 4]]

Related

Find minimum sum

Find the minimum sum of elements with one element from each row. I think the answer is
-214, but z3py returns unsat. What is wrong?
from z3 import Solver, Int, ForAll, Or
ARR = [
[36, 12, 90, 88, 82],
[-92, 50, 40, 31, 43],
[81, 28, -26, 8, -59],
[18, -99, -70, -33, 58],
[44, -33, 24, -92, -68],
]
s = Solver()
xs = [Int(f"x_{i}") for i, row in enumerate(ARR)]
ys = [Int(f"y_{i}") for i, row in enumerate(ARR)]
for x, y, row in zip(xs, ys, ARR):
s.add(Or(*[x == val for val in row]))
s.add(Or(*[y == val for val in row]))
s.add(ForAll(ys, sum(xs) <= sum(ys)))
print(s.check()) # unsat
Your encoding isn't quite correct. If you stick the following line in your program:
print(s.sexpr())
You'll see that it prints, amongst other things:
(assert (forall ((y_0 Int) (y_1 Int) (y_2 Int) (y_3 Int) (y_4 Int))
(<= (+ 0 x_0 x_1 x_2 x_3 x_4) (+ 0 y_0 y_1 y_2 y_3 y_4))))
And this is the reason why it is unsat. This is a quantified formula, and thus it says it is only satisfiable if the formula is true for all values y_0 .. y_4. This is obviously not true, and hence the unsat result.
Instead of this formulation, you should use z3's optimization engine. Pick one variable from each row, add them, and minimize that result. Something like this:
from z3 import *
ARR = [
[36, 12, 90, 88, 82],
[-92, 50, 40, 31, 43],
[81, 28, -26, 8, -59],
[18, -99, -70, -33, 58],
[44, -33, 24, -92, -68],
]
o = Optimize()
es = [Int(f"e_{i}") for i, row in enumerate(ARR)]
for e, row in zip (es, ARR):
o.add(Or(*[e == val for val in row]))
minTotal = Int("minTotal")
o.add(minTotal == sum(es))
o.minimize(minTotal)
print(o.check())
print(o.model())
When I run this, I get:
sat
[e_0 = 12,
e_3 = -99,
e_2 = -59,
e_1 = -92,
e_4 = -92,
minTotal = -330]
That is, solver picks 12 from the first row, -92 from the second, -59 from the third, -99 from the fourth, and -92 from the last row; for a minimum sum of -330.
It's easy to see that this is the correct solution since the solver picks minimum element from each row, and thus their sum will be minimal as well. (I'm not sure why you were expecting -214 to be the answer.)

How to generate conditions within constraints in Z3py

Let us assume there are 5-time slots and at each time slot, I have 4 options to choose from, each with a known reward, for eg. rewards = [5, 2, 1, -3]. At every time step, at least 1 of the four options must be selected, with a condition that, if option 3 (with reward -3) is chosen at a time t, then for the remaining time steps, none of the options should be selected. As an example, considering the options are indexed from 0, both [2, 1, 1, 0, 3] and [2, 1, 1, 3, 99] are valid solutions with the second solution having option 3 selected in the 3rd time step and 99 is some random value representing no option was chosen.
The Z3py code I tried is here:
T = 6 #Total time slots
s = Solver()
pick = [[Bool('t%d_ch%d' %(j, i)) for i in range(4)] for j in range(T)]
# Rewards of each option
Rewards = [5, 2, 1, -3]
# Select at most one of the 4 options as True
for i in range(T):
s.add(Or(Not(Or(pick[i][0], pick[i][1], pick[i][2], pick[i][3])),
And(Xor(pick[i][0],pick[i][1]), Not(Or(pick[i][2], pick[i][3]))),
And(Xor(pick[i][2],pick[i][3]), Not(Or(pick[i][0], pick[i][1])))))
# If option 3 is picked, then none of the 4 options should be selected for the future time slots
# else, exactly one should be selected.
for i in range(len(pick)-1):
for j in range(4):
s.add(If(And(j==3,pick[i][j]),
Not(Or(pick[i+1][0], pick[i+1][1], pick[i+1][2], pick[i+1][3])),
Or(And(Xor(pick[i+1][0],pick[i+1][1]), Not(Or(pick[i+1][2], pick[i+1][3]))),
And(Xor(pick[i+1][2],pick[i+1][3]), Not(Or(pick[i+1][0], pick[i+1][1]))))))
if s.check()==False:
print("unsat")
m=s.model()
print(m)
With this implementation, I am not getting solutions such as [2, 1, 1, 3, 99]. All of them either do not have option 3 or have it in the last time slot.
I know there is an error inside the If part but I'm unable to figure it out. Is there a better way to achieve such solutions?
It's hard to decipher what you're trying to do. From a basic reading of your description, I think this might be an instance of the XY problem. See https://xyproblem.info/ for details on that, and try to cast your question in terms of what your original goal is; instead of a particular solution, you're trying to implement. (It seems to me that the solution you came up with is unnecessarily complicated.)
Having said that, you can solve your problem as stated if you get rid of the 99 requirement and simply indicate -3 as the terminator. Once you pick -3, then all the following picks should be -3. This can be coded as follows:
from z3 import *
T = 6
s = Solver()
Rewards = [5, 2, 1, -3]
picks = [Int('pick_%d' % i) for i in range(T)]
def pickReward(p):
return Or([p == r for r in Rewards])
for i in range(T):
if i == 0:
s.add(pickReward(picks[i]))
else:
s.add(If(picks[i-1] == -3, picks[i] == -3, pickReward(picks[i])))
while s.check() == sat:
m = s.model()
picked = []
for i in picks:
picked += [m[i]]
print(picked)
s.add(Or([p != v for p, v in zip(picks, picked)]))
When run, this prints:
[5, -3, -3, -3, -3, -3]
[1, 5, 5, 5, 5, 1]
[1, 2, 5, 5, 5, 1]
[2, 2, 5, 5, 5, 1]
[2, 5, 5, 5, 5, 1]
[2, 1, 5, 5, 5, 1]
[1, 1, 5, 5, 5, 1]
[2, 1, 5, 5, 5, 2]
[2, 5, 5, 5, 5, 2]
[2, 5, 5, 5, 5, 5]
[2, 5, 5, 5, 5, -3]
[2, 1, 5, 5, 5, 5]
...
I interrupted the above as it keeps enumerating all the possible picks. There are a total of 1093 of them in this particular case.
(You can get different answers depending on your version of z3.)
Hope this gets you started. Stating what your original goal is directly is usually much more helpful, should you have further questions.

Broadcast Schedule Selection

I have an 11 week game schedule for 11 teams (5 games each week). I need to try to select from that list 11 games (1 each week) that provide each of the 11 teams with a broadcast of one home and one away game. Ideally this would be code that I would be able to reuse for future years and that I could scale to more teams and weeks if necessary.
I know that the likelihood of finding a viable solution for a given, already created schedule is extremely low, and, in many cases there doesn't exist a solution. So, when a solution of the type listed above doesn't exist, I would like to get a schedule that come close. That is, one in which all the teams get two broadcasts, but some teams may get two home or two away games instead of one of each.
I've looked a several different approaches. I have a number of 5x2 (Away Team, Home Team) arrays (weekly matchups) that I've tried to run a sort/selection with conditions (like a_1 =\= a_j j>1 and a_i in {1..11}) on, but I can't figure out how to get the double restriction selection to work, and I can't figure out how to make it go back to a previous selection when it has no more viable selections. I've tried to brute force it, but 40 million possible combinations is more than I can handle.
I'm using MATLab to perform all the work. I can usually translate from C or C++ to MATLab usable code.
This seemed like a fun problem so I took a crack at formulating it as an IP.
Let J and T be the set of teams and weeks.
Let G be the set of all games; each element of G is a tuple (i,j,t) that indicates the away team (i), the home team (j), and the week (t).
Let H be the set of all home games; each element of H is a tuple (j,t) that indicates the home team (j) and the week (t).
Define the following binary decision variables:
w[j,t] = 1 if we broadcast the home game at j in week t, = 0 otherwise (defined for (j,t) in H)
x[j] = 1 if team j has an away-game broadcast, = 0 otherwise (defined for j in J)
y[j] = 1 if team j has a home-game broadcast, = 0 otherwise (defined for j in J)
z[j] = 1 if team j has both an away-game and a home-game broadcast, = 0 otherwise (defined for j in J)
Then the model is:
maximize sum {j in J} z[j]
subject to sum {j in J} w[j,t] = 1 for all t
x[j] <= sum {(i,t) in H: (j,i,t) in G} w[i,t] for all j
y[j] <= sum {t in T} w[j,t] for all j
z[j] <= (1/2) * (x[j] + y[j]) for all j
w[j,t], x[j], y[j], z[j] in {0,1}
The objective function calculates the total number of teams that get both a home and an away broadcast. The first constraint says we need exactly one broadcast per week. The second constraint says x[j] can't equal 1 unless there is some week when j's away game gets broadcast. The third constraint says the same for y[j] and the home broadcast. The fourth constraint says z[j] can't equal 1 unless both x[j] and y[j] equal 1. The last constraint says everything has to be binary.
I coded this model in Python/PuLP using an 11-game schedule. (Obviously you'd plug in your own schedule.)
from pulp import *
import numpy as np
# Number of teams, weeks, and games per week.
num_teams = 11
num_weeks = 11
num_games_per_week = 5
# Lists of teams and weeks.
teams = range(1, num_teams+1)
weeks = range(1, num_weeks+1)
# List of game tuples: (i, j, t) means team i plays at team j in week t.
games = [(1, 10, 1), (2, 9, 1), (3, 8, 1), (4, 7, 1), (5, 6, 1),
(6, 4, 2), (7, 3, 2), (8, 2, 2), (9, 1, 2), (10, 11, 2),
(2, 11, 3), (3, 10, 3), (4, 9, 3), (5, 8, 3), (6, 7, 3),
(7, 5, 4), (8, 4, 4), (9, 3, 4), (10, 2, 4), (11, 1, 4),
(3, 1, 5), (4, 11, 5), (5, 10, 5), (6, 9, 5), (7, 8, 5),
(8, 6, 6), (9, 5, 6), (10, 4, 6), (11, 3, 6), (1, 2, 6),
(4, 2, 7), (5, 1, 7), (6, 11, 7), (7, 10, 7), (8, 9, 7),
(9, 7, 8), (10, 6, 8), (11, 5, 8), (1, 4, 8), (2, 3, 8),
(5, 3, 9), (6, 2, 9), (7, 1, 9), (8, 11, 9), (9, 10, 9),
(10, 8, 10), (11, 7, 10), (1, 6, 10), (2, 5, 10), (3, 4, 10),
(11, 9, 11), (1, 8, 11), (2, 7, 11), (3, 6, 11), (4, 5, 11)]
# List of home games: (j, t) means there is a home game at j in week t.
home_games = [(j, t) for (i, j, t) in games]
# Initialize problem.
prob = LpProblem('Broadcast', LpMaximize)
# Generate decision variables.
w = LpVariable.dicts('w', home_games, 0, 1, LpInteger)
x = LpVariable.dicts('x', teams, 0, 1, LpInteger)
y = LpVariable.dicts('y', teams, 0, 1, LpInteger)
z = LpVariable.dicts('z', teams, 0, 1, LpInteger)
# Objective function.
prob += lpSum([z[j] for j in teams])
# Constraint: 1 broadcast per week.
for t in weeks:
prob += lpSum([w[j, t] for j in teams if (j, t) in home_games]) == 1
# Constraint: x[j] can only = 1 if we broadcast a game in which j is away team.
for j in teams:
prob += x[j] <= lpSum([w[i, t] for (i, t) in home_games if (j, i, t) in games])
# Constraint: y[j] can only = 1 if we broadcast a game in which j is home team.
for j in teams:
prob += y[j] <= lpSum(([w[j, t] for t in weeks if (j, t) in home_games]))
# Constraint: z[j] can only = 1 if x[j] and y[j] both = 1.
for j in teams:
prob += z[j] <= 0.5 * (x[j] + y[j])
# Solve problem.
prob.solve()
# Print status.
print("Status:", LpStatus[prob.status])
# Print optimal values of decision variables.
for v in prob.variables():
if v.varValue is not None and v.varValue > 0:
print(v.name, "=", v.varValue)
# Prettier print.
print("\nNumber of teams with both home and away broadcasts: {:.0f}".format(np.sum([z[j].value() for j in teams])))
for (i, j, t) in games:
if w[j, t].value() == 1:
print("Week {:2d}: broadcast team {:2d} at team {:2d}".format(t, i, j))
The results are:
Number of teams with both home and away broadcasts: 11
Week 1: broadcast team 1 at team 10
Week 2: broadcast team 10 at team 11
Week 3: broadcast team 5 at team 8
Week 4: broadcast team 8 at team 4
Week 5: broadcast team 6 at team 9
Week 6: broadcast team 11 at team 3
Week 7: broadcast team 4 at team 2
Week 8: broadcast team 9 at team 7
Week 9: broadcast team 7 at team 1
Week 10: broadcast team 2 at team 5
Week 11: broadcast team 3 at team 6
You can see that each team gets both a home and an away broadcast.

caption not aligned with table in longtable latex landscape mode

In my longtable when I use the landscape mode, the caption is not aligned with the table width. Although when I removed the landscape mode, it is fine. However, my table is really wide and long, and I need to align it with landscape mode. Here is a MWE, please help me out here.
Thanks
\documentclass{article}
\usepackage{lipsum} % Just to fill some paragraph with random texts.
\usepackage[margin=1in,left=1.5in,includefoot]{geometry} % Setup the margin of the document \usepackage{float}
\usepackage{longtable}
\usepackage{booktabs}
\usepackage{tabu, colortbl}
\usepackage{array}
\usepackage{graphicx}
\usepackage{rotating}
\usepackage{lscape}
\usepackage[referable]{threeparttablex}
\usepackage{amsmath}
\usepackage{parskip}
\usepackage{xcolor}
\usepackage{setspace}
\renewcommand{\arraystretch}{1.5}
\setlength{\tabcolsep}{2pt}
\tabulinesep=1mm
\usepackage{ltcaption}
% The ltcaption package supports \CaptionLabelFont & \CaptionTextFont introduced by the NTG document classes
%\renewcommand\LTcaptype{table}
\renewcommand\CaptionLabelFont{\normalsize}
\renewcommand\CaptionTextFont{\normalsize}
\setlength{\LTcapwidth}{\textwidth} % caption width is same as table width
\setlength\LTcapskip{0.25em} % otherwise caption will skip baseline between caption and content
\renewcommand{\arraystretch}{1.5} % Insert space between table rows
\setlength{\tabcolsep} {1.3pt}
\tabulinesep = 1.2 mm
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Document begins here
\begin{document}
\begin{ThreePartTable}
\begin{landscape}
\begin{singlespace}
\begin{longtabu} to \linewidth {X[1,L]X[1,L]X[1,L]}
\noalign{\phantomsection}\caption[Feasible triples for a highly variable Grid]{Feasible triples for
highly variable Grid, MLMMH. This is really really a long caption. This is really really a long caption. This is really really a long caption. This is really really a long caption. This is really really a long caption. This is really really a long caption. This is really really a long caption} \label{tbl} \\
\hline \multicolumn{1}{l}{\textbf{Time (s)}} & \multicolumn{1}{l}{\textbf{Triple chosen}} & \multicolumn{1}{l}{\textbf{Other feasible triples}} \\ \hline
\endfirsthead
\multicolumn{3}{l}%
{{\bfseries \tablename\ \thetable{} -- continued from previous page}} \\
\hline \multicolumn{1}{l}{\textbf{Time (s)}} &
\multicolumn{1}{l}{\textbf{Triple chosen}} &
\multicolumn{1}{l}{\textbf{Other feasible triples}} \\ \hline
\endhead
\hline \multicolumn{3}{l}{{Continued on next page}} \\ \hline
\endfoot
\hline \hline
\endlastfoot
0 & (1, 11, 13725) & (1, 12, 10980), (1, 13, 8235), (2, 2, 0), (3, 1, 0) \\
2745 & (1, 12, 10980) & (1, 13, 8235), (2, 2, 0), (2, 3, 0), (3, 1, 0) \\
5490 & (1, 12, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
8235 & (1, 12, 16470) & (1, 13, 13725), (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
10980 & (1, 12, 16470) & (1, 13, 13725), (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
13725 & (1, 12, 16470) & (1, 13, 13725), (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
16470 & (1, 13, 16470) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
19215 & (1, 12, 16470) & (1, 13, 13725), (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
21960 & (1, 12, 16470) & (1, 13, 13725), (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
24705 & (1, 12, 16470) & (1, 13, 13725), (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
27450 & (1, 12, 16470) & (1, 13, 13725), (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
30195 & (2, 2, 2745) & (2, 3, 0), (3, 1, 0) \\
32940 & (1, 13, 16470) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
35685 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
38430 & (1, 13, 10980) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
41175 & (1, 12, 13725) & (1, 13, 10980), (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
43920 & (1, 13, 10980) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
46665 & (2, 2, 2745) & (2, 3, 0), (3, 1, 0) \\
49410 & (2, 2, 2745) & (2, 3, 0), (3, 1, 0) \\
52155 & (1, 12, 16470) & (1, 13, 13725), (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
54900 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
57645 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
60390 & (1, 12, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
63135 & (1, 13, 16470) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
65880 & (1, 13, 16470) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
68625 & (2, 2, 2745) & (2, 3, 0), (3, 1, 0) \\
71370 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
74115 & (1, 12, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
76860 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
79605 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
82350 & (1, 12, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
85095 & (1, 12, 13725) & (1, 13, 10980), (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
87840 & (1, 13, 16470) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
90585 & (1, 13, 16470) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
93330 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
96075 & (1, 13, 16470) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
98820 & (1, 13, 16470) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
101565 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
104310 & (1, 13, 16470) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
107055 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
109800 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
112545 & (1, 12, 16470) & (1, 13, 13725), (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
115290 & (1, 13, 16470) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
118035 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
120780 & (1, 13, 16470) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
123525 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
126270 & (1, 12, 16470) & (1, 13, 13725), (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
129015 & (2, 2, 2745) & (2, 3, 0), (3, 1, 0) \\
131760 & (2, 2, 2745) & (2, 3, 0), (3, 1, 0) \\
134505 & (1, 13, 16470) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
137250 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
139995 & (2, 2, 2745) & (2, 3, 0), (3, 1, 0) \\
142740 & (2, 2, 2745) & (2, 3, 0), (3, 1, 0) \\
145485 & (1, 12, 16470) & (1, 13, 13725), (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
148230 & (2, 2, 2745) & (2, 3, 0), (3, 1, 0) \\
150975 & (1, 13, 16470) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
153720 & (1, 12, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
156465 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
159210 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
161955 & (1, 13, 16470) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
164700 & (1, 13, 13725) & (2, 2, 2745), (2, 3, 0), (3, 1, 0) \\
\end{longtabu}
\end{singlespace}
\end{landscape}
\end{ThreePartTable}
\end{document}
Any of the following fixes:
Overwrite \LTcapwidth as follows below, just as your landscape environment begin
\begin{ThreePartTable}
\begin{landscape}
\setlength\LTcapwidth{\linewidth}
...
Or, simply remove the \setlength command for \LTcapwidth in the header of your document
% \setlength{\LTcapwidth}{\textwidth}
Yields the following result:
Generally, for landscape mode, use \linewidth rather than \textwidth.

ruby/rails array all elements between two indices

I have an array like this: [7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6]
What's the simplest way to return each item in the array from position 6 until 0 where the resulting array looks like: [1,2,3,4,5,6,7]
This positions in the array can be dynamic, for example passing in 4 and 9 should return [11,12,1,2,3,4]
I'm wondering if there's a method that accomplishes this in Rails api.
Thanks in advance
EDIT
Let's assume that no negative numbers, so doing array[2..-2] wont work.
Array#splice almost works for this, but if the second position is less than the first, it returns nil.
def foo a, min, max
a.rotate(min).first((max - min) % a.length + 1)
end
a = [7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6]
foo(a, 6, 0) # => [1, 2, 3, 4, 5, 6, 7]
foo(a, 4, 9) # => [11, 12, 1, 2, 3, 4]
class Array
def get_sub_array(start,last)
(start > last) ? (self[start..-1] + self[0..last]) : self[start..last]
end
end
Then
a = [7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6]
a.get_sub_array(6,0)
#[1, 2, 3, 4, 5, 6, 7]
Or if you don't want to monkey patch
You could have a method like
def get_sub_array(array, start,last)
(start > last) ? (array[start..-1] + array[0..last]) : array[start..last]
end
a = [7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6]
get_sub_array(a,6,0)
#[1, 2, 3, 4, 5, 6, 7]
def some_function(some_array,start_val=6, end_val=0)
if end_val > start_val
some_array[start_val,(end_val - start_val)]
else
(some_array[start_val, some_array.size] << some_array[0, (end_val)]).flatten
end
end
You can use ternary operator to make it one liner too:
def some_function(some_array,start_val=6, end_val=0)
end_val > start_val ? some_array[start_val,(end_val - start_val)] : (some_array[start_val, some_array.size] << some_array[0, (end_val)]).flatten
end
a = [7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6]
some_function(a) # => [1, 2, 3, 4, 5, 6, 7]
some_function(a, 4, 9) # => [11, 12, 1, 2, 3, 4]
min=6
max=0
arr = [7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6]
result = []
if max<min
result << arr[min..arr.length]
result << arr[0..max]
else
result << arr[min..max]
end
A couple more ways (my preference being for #1).
a = [7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6]
#1
def foo a, min, max
as = a.size
max += as if max < min
(min..max).map { |i| a[i%as] }
end
foo(a, 6, 0) # => [ 1, 2, 3, 4, 5, 6, 7]
foo(a, 4, 9) # => [11, 12, 1, 2, 3, 4]
#2
def foo a, min, max
max += a.size if max < min
e = a.cycle
min.times { e.next }
(max-min+1).times.map { e.next }
end
foo(a, 6, 0) # => [ 1, 2, 3, 4, 5, 6, 7]
foo(a, 4, 9) # => [11, 12, 1, 2, 3, 4]
def foo a, s, e
a = e < s ? (a[s,a.size] << a[0..e]).flatten : a[s..e]
end
a = [7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6]
a = foo(a, 6, 0) # => [1, 2, 3, 4, 5, 6, 7]
a = foo(a, 4, 9) # => [11, 12, 1, 2, 3, 4]
myArray = [7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6]
myArray[6..-1] returns [1, 2, 3, 4, 5, 6]
myArray[4..9] returns [11,12,1,2,3,4]

Resources