0

I have two arrays generated from :

@dividends_values = @dividends.historical.map(&:dividend).reverse.last(50)
@dividends_dates = @dividends.historical.map(&:date).reverse.last(50)

The first array is an array of float values and occasional there can be a few nil entries in that. I want to remove those nil entries(which is pretty easy with a compact or something like that), but I also want to move the corresponding entries from the @dividends_dates array.

That is because they the dates array is a 1-1 reference to the values array, so index 0 of array with dates correspondings to index 0 of array with values.

What is a good way to do that?

7
  • If @dividends_values[3] is nil then also remove @dividends_dates[3]? Commented May 1, 2022 at 20:01
  • Do you want to wind up with 50 entries in each? Or is it ok there are less than 50? Commented May 1, 2022 at 20:02
  • yes exactly. if @dividends_values[3] is nil it should also remove @dividends_dates[3], that's right Commented May 1, 2022 at 20:05
  • Make one array of [dividend, date] pairs and remove all entries with a dividend (first value) of nil. Afterwards separate the pairs. Commented May 1, 2022 at 20:06
  • the 50 is just the sample i am getting, but is irrelevant to the question really. You can ignore reverse and last(50) there. Commented May 1, 2022 at 20:06

3 Answers 3

3

First, filter by nil. Then break that up into two arrays.

@last_dividends = @dividends.historical.select { |d| d.dividend }
@dividends_values = @last_dividends.map(&:dividend)
@dividends_dates = @last_dividends.map(&:date)

Better yet, turn them into a single array of [[dividend, date], [...]]

@last_dividends = @dividends
  .historical
  .select { |d| d.dividend }
  .map { |d| [d.dividend, d.date] }
Sign up to request clarification or add additional context in comments.

2 Comments

Actually i need to preserve the two arrays, although i guess i could transpose. But yeah, i am a moron, i should have removed the whole object yeah :D Thanks for that !
We can filter_map now so the second option can be simplified to .filter_map {|d| [d.dividend, d.date] if d.dividend }
1

First let's create a class-like object for illustration.

Dividend = Struct.new(:value, :date)
historical = [
  Dividend.new(nil, "Jan 1"),
  Dividend.new(10,  "Mar 22"),
  Dividend.new(13,  "Apr 21"),
  Dividend.new(nil, "Aug 7"),
  Dividend.new(8,   "Oct 11")
]
  #=> [#<struct Dividend value=nil, dade="Jan 1">,
  #    #<struct Dividend value=10,  date="Mar 22">,
  #    #<struct Dividend value=13,  date="Apr 21">,
  #    #<struct Dividend value=nil, date="Aug 7">,
  #    #<struct Dividend value=8,   date="Oct 11">]

Then, for example,

  inst = historical[3]
    #=> #<struct Dividend value=nil, date="Aug 7">
  inst.value
    #=> nil
  inst.date
    #=> "Aug 7"

We may write

historical.filter_map do |inst|
  [inst.value, inst.date] unless inst.value.nil?
end.transpose
  #=> [[10, 13, 8], ["Mar 22", "Apr 21", "Oct 11"]]

Note that

historical.filter_map do |inst|
  [inst.value, inst.date] unless inst.value.nil?
end
  #=> [[10, "Mar 22"], [13, "Apr 21"], [8, "Oct 11"]]

See Enumerable#filter_map.

Comments

0

If you have two arrays with corresponding elements, e.g.:

values = [1, nil, 3]
dates = [Date.new(2022, 5, 1), Date.new(2022, 5, 2), Date.new(2022, 5, 3)]

You can turn them into one combined array of [dividend, date] pairs by using transpose:

values_with_dates = [values, dates].transpose
#=> [[1, #<Date: 2022-05-01>], [nil, #<Date: 2022-05-02>], [3, #<Date: 2022-05-03>]]

You can then remove the elements with value of nil via reject!:

values_with_dates.reject! { |value, date| value.nil? }
#=> [[1, #<Date: 2022-05-01>], [3, #<Date: 2022-05-03>]]

And transpose again to separate the pairs:

values_with_dates.transpose
#=> [[1, 3], [#<Date: 2022-05-01>, #<Date: 2022-05-03>]]

The inner arrays can be assigned back to separate variables using Ruby's multiple assignment.

As a one-liner:

values, dates = [values, dates].transpose.reject { |v, _| v.nil? }.transpose

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.