0

I have an array in this format:

[ 
  { day: 1
    intervals: [
      {
        from: 900,
        to: 1200
      }
    ]
  },
  { day: 2
    intervals: [
      {
        from: 900,
        to: 1200
      }
    ]
  },
  { day: 3
    intervals: [
      {
        from: 900,
        to: 1200
      }
    ]
  },
  { day: 4
    intervals: [
      {
        from: 900,
        to: 1200
      }
    ]
  },
  { day: 5
    intervals: [
      {
        from: 900,
        to: 1200
      }
    ]
  },
  { day: 6
    intervals: [
      {
        from: 900,
        to: 1200
      },
      {
        from: 1300,
        to: 2200
      }
    ]
  },
  { day: 7
    intervals: [
      {
        from: 900,
        to: 1200
      },
      {
        from: 1300,
        to: 2200
      }
    ]
  }
]

I wan't to group them like this:

[ 
  { day: 1-5
    intervals: [
      {
        from: 900,
        to: 1200
      }
    ]
  },
  { day: 6-7
    intervals: [
      {
        from: 900,
        to: 1200
      },
      {
        from: 1300,
        to: 2200
      }
    ]
  }
]

Criteria:

  • Only group if intervals are the same.
  • Only group if matches are in chronological order, i.e 1-5 or 1-3, not 1-2-5.

How can this be achieved?

2
  • Note that your example output does not fulfill the 2nd requirement. 6-7 is only 2 days. Commented Apr 2, 2012 at 15:01
  • Oh, you're right! Deleted that criteria. Thanks! Commented Apr 2, 2012 at 15:05

4 Answers 4

2

Here's a variation of @joelparkerhenderson's solution that tries to be a bit closer to your requirements re formatting of the output etc.

output = []

grouped = input.group_by do |x|
  x[:intervals]
end

grouped.each_pair do |k, v|
  days = v.map {|day| day[:day]}
  if days.each_cons(2).all? { |d1, d2| d1.next == d2 }
    output << {
      :days => days.values_at(0,-1).join('-'),
      :intervals => k
    }
  end
end

puts output
Sign up to request clarification or add additional context in comments.

3 Comments

If days 1, 2, 3, 5 have the same intervals, your code will not group 1-3, right?
True enough, this certainly can be improved.
I found this to be the best starting point for my problem, thanks a lot!
1

This produces the required output:

by_interval = data.inject({}) do | a, e |
  i = e[:intervals]
  a[i] ||= []
  a[i] << e[:day].to_i
  a
end

result = by_interval.map do | interval, days |
  slices = days.sort.inject([]) do | a, e |
    a << [] if a == [] || a.last.last != e - 1
    a.last << e
    a
  end
  slices.map do | slice |
      {:day => "#{slice.first}-#{slice.last}", :intervals => interval }
  end
end
result.flatten!

I'm sure there are better approaches :-)

Comments

0

You need to look into Map Method for an array. You need to remap the array and iterate over it to extract the data you want using your "grouping" logic above.

1 Comment

Hi, thanks for your answer, I do know about map/collect, Having trouble comparing all the day hashes with each other though.
0

Extending @Michael Kohl's answer

output = []

grouped = schedules.as_json.group_by do |x|
  x['intervals']
end

grouped.each_pair do |k, v|
  days = v.map {|day| day['day']}
  grouped_days = days.inject([[]]) do |grouped, num|
    if grouped.last.count == 0 or grouped.last.last == num - 1
      grouped.last << num
    else
      grouped << [ num ]
    end
    grouped
  end

  grouped_days.each do |d|
    output << {
      heading: d.values_at(0, -1).uniq.join('-'),
      interval: k
    }
  end
end

output

You should probably split that up into separate methods, but you get the idea.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.