Spyros
Spyros

Reputation: 48606

Remove nil values from array and corresponding entries from reference array

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?

Upvotes: 0

Views: 216

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110665

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.

Upvotes: 1

Stefan
Stefan

Reputation: 114138

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

Upvotes: 0

Schwern
Schwern

Reputation: 164659

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] }

Upvotes: 3

Related Questions