-3

Example:

Here is binary numbers array:

a = [001, 010, 100, 011, 101, 110, 111, 1000, 1001, 1010]

I want output like below:

[ [ 001, 010, 100, 1000 ], [ 011, 101, 110, 1001, 1010 ], [ 111 ] ]

Can anybody help me how to achieve it in ruby?

Stefan
  • 109,145
  • 14
  • 143
  • 218
asha
  • 139
  • 1
  • 6
  • 2
    Is that literally what the array looks like or is it made of strings? Because if it's literal numbers then they're not *actually* in binary and things are going to be a bit awkward. – Silvio Mayolo Dec 13 '17 at 05:34
  • What is the logic behind the grouping? – Santhosh Dec 13 '17 at 05:45
  • @Santhosh It's in the title. It counts the number of `1` in each string. – Silvio Mayolo Dec 13 '17 at 05:45
  • 1
    Numbers with leading `0` are interpreted as octal numbers, so `010` is `8`. – Stefan Dec 13 '17 at 06:42
  • 1
    What is the code you are having trouble with? What trouble do you have with your code? Do you get an error message? What is the error message? Is the result you are getting not the result you are expecting? What result do you expect and why, what is the result you are getting and how do the two differ? Is the behavior you are observing not the desired behavior? What is the desired behavior and why, what is the observed behavior, and in what way do they differ? Please, provide a [mcve]. – Jörg W Mittag Dec 13 '17 at 09:08
  • 1
    Can you provide a *precise* specification of what it is that you want to happen, including any and all rules, exceptions from those rules, corner cases, special cases, boundary cases, and edge cases? Can you provide sample inputs and outputs demonstrating what you expect to happen, both in normal cases, and in all the exceptions, corner cases, special cases, boundary cases, and edge cases? Please, also provide a [mcve]. – Jörg W Mittag Dec 13 '17 at 09:08
  • 1
    "Can anybody help me how to achieve it in ruby?" – You can achieve it by writing a program which does that. If you have a problem with your program, carefully read the documentation of all the methods, classes, modules, and libraries you are using, write tests for your programs, trace the execution with pen and paper, single-step it in a debugger, then sleep on it, start again from the beginning, sleep on it again, and *then and only then* narrow your problem down to a concise, focused, simple, short, reproducible [mcve] and ask a focused, narrow question on [so]. – Jörg W Mittag Dec 13 '17 at 09:09

2 Answers2

2

I'm going to assume you're working with strings ("001") and not decimal/octal literals (001). If that's not the case, I strongly suggest casting to strings to make things easier on you.

We can count the number of ones in a string x with x.count('1'). Then we can take a list of strings and organize it by this value with a.group_by(...). This gives a hash, so if you just want the values (as your suggested output suggests), then you simply take the values of it.

a.group_by { |x| x.count('1') }.values
Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
  • Thanks @silvio. I have converted into string and did like above, it worked. – asha Dec 13 '17 at 05:46
  • 2
    If you were working with integers, e.g. `a = [0b001, 0b010, ...]`, you could find the number of set bits in a similar fashion: `a.group_by { |n| n.digits(2).count(1) }.values` – Stefan Dec 13 '17 at 06:55
  • Note that the solution @Stefan provides uses `Integer#digits` which requires Ruby 2.4. – Mark Thomas Dec 13 '17 at 12:31
0

Using Enumerable#group_by, as @Silvio has done, seems the most direct way to solve this problem, but here are a couple of other approaches one could use.

a = "001, 010, 100, 011, 101, 110, 111, 1000, 1001, 1010".split(', ')
  #=> ["001", "010", "100", "011", "101", "110", "111", "1000", "1001", "1010"]

Construct a hash whose keys, k, are numbers of ones and whose values are arrays containing the elements from the original array whose numbers of one1 equal k

a.each_with_object({}) { |s,h| (h[s.count('1')] ||= []) << s }.values
  #=> [["001", "010", "100", "1000"], ["011", "101", "110", "1001", "1010"], ["111"]]

Note values is applied to the hash returned by the block, namely

{1=>["001", "010", "100", "1000"], 2=>["011", "101", "110", "1001", "1010"], 3=>["111"]}

Consider the expression, (h[s.count('1')] ||= []) << s. Let

cnt = s.count('1')

Then (h[cnt] ||= []) << s expands to the following when parsed.

(h[cnt] = h[cnt] || []) << s

If h does not have a key cnt, then h[cnt] on the right of the equality equals nil, so the expression reduces to

(h[cnt] = []) << s

so h[cnt] #=> [s]. On the other hand, if h does have a key cnt, h[cnt] equals an array, which is truthy, so we execute

h[cnt] << s

Note that in h[cnt] = h[cnt] || [], the method on the left of the equality is Hash#[]=, whereas we have Hash#[] is on the right of the equality.

Sort then slice

a.sort_by { |s| s.count('1') }.slice_when { |s1,s2| s1.count('1') < s2.count('1') }.to_a
  #=> [["001", "010", "100", "1000"], ["011", "101", "110", "1001", "1010"], ["111"]]
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100