4

So I am trying to return another String from 2 input sentences zipped together. If the 2 sentences are identical in length it will produce an actual output. If the two input sentences are not identical in length then it will just return an empty string. Here is my code so far but I can't figure out how to zip the words correctly could someone help me. BTW it would be nice if you could help me by doing this recursively because I'm trying to practice that.

Ex:

Zippppp("ABC", "123") will return "A1B2C3"
Zippppp("AD", "CCC") will return “”
public class Zippppp
{
    public Zippppp(String a, String s)
    {
       int l1 = a.length();
       int l2 = s.length();
       if(l1 == l2)
          for(int i = 0; i > l1; i++)          
             System.out.print( a.substring(0, 1) + s.substring(0, 1));
     }
      
     public static void main(String args[ ])
     {
        Zippppp sv = new Zippppp("ABC", "123");
        System.out.print(sv);
     }
}
aran
  • 10,978
  • 5
  • 39
  • 69
ozmrur
  • 75
  • 3
  • No need for your for loop and the substring method calls after the `if` condition that checks if they're the same length. Simply put `System.out.println(a + s);` after your `if` statement. – CAMD_3441 Feb 22 '21 at 03:12
  • Also noticed after that you asked about doing it recursively. However thats not what you have in your code, recursion is a method that invokes invokes itself repeatedly. So your `Zippppp` method would have the statement `return Zippppp(param1, param2)` within it. – CAMD_3441 Feb 22 '21 at 03:15
  • @ozmrur, If using a library is an option, you can use `Guava`.zip. Also, care should be taken to handle unicode characters appropriately – Thiyanesh Feb 22 '21 at 03:50
  • Similar (but is a debug code question) : https://stackoverflow.com/questions/25084200/alternate-display-of-2-strings-in-java#25084271 – user202729 Feb 22 '21 at 06:49

3 Answers3

11

I love your class name. Sincerely

In order to really "return" it, you could implement something similar to the examples below.

Update/Edit: The original answer is below, as the three new approaches (which don't care about the number of strings to zip) are on top.


[MultiThreaded]

The ultimate ZIPPER

Each of the words to zip is handled by a thread. Why? Ask yourself: WHY NOT???

Boredom makes this things happen.

Each word will be lovely processed by its own thread. Threads organize themselves in order not to process the same word and set the same positions thanks to the AtomicInteger.

String[] mix =new String[]{"AAAZZZ","100001","BBBWWW","200002","CCCYYY","300003",
                           "DDDXXX", "400004","EEEWWW","5000005","FFFVVV","600006"};

int strl = mix[0].length();        //entry's length
int nwords = mix.length;           //number of strings
char[] zip=new char[strl*nwords];  //the result

AtomicInteger myWord = new AtomicInteger(0);
//returning boolean if want to check some future failed(not here lol)
List<Callable<Boolean>> callables = new ArrayList<>(nwords);  
Callable<Boolean> zipYours =
    new Callable<Boolean>() 
    {  
        public Boolean call() 
        {  
           try
           {
              int mine = myWord.getAndIncrement();
              for (int x=0; x < strl; x++)
                  zip[mine+(nwords*x)]=mix[mine].charAt(x);

            }catch(Exception e) {
               return false;
            }               
           
            return true;
         }  
     };
         
 for (int i=0;i<nwords;i++)
      callables.add(zipYours);
     
 //one thread - one word - true loef
 ExecutorService executor = Executors.newFixedThreadPool(nwords);
 executor.invokeAll(callables);  
 executor.shutdown();  

 System.out.println(new String(zip));
 /*A1B2C3D4E5F6A0B0C0D0E0F0A0B0C0D0E0F0Z0W0Y0X0W0V0Z0W0Y0X0W0V0Z1W2Y3X4W0V6*/

Was this required by any means? OF COURSE NOT. But it was fun, and my girlfriend told me to do it.

Lie, I don't have a girlfriend. Do you really think I'd be doing this if I had one??


[Zip'em all]

Two different approaches:

1. Direct move

Works regardless of the number of strings to zip, from 2 to n.* This means these approaches are also the replacement of the old methods, as you can either call getStringsZippedDirectMove("ABC,"123") or getStringsZippedDirectMove(yourArray).

In this approach, each string is completely allocated at a time, so each element in the list is only accessed/processed once. The main loop iterates based on the number of elements in the array:

public static String getStringsZippedDirectMove(String... mix) 
{    
   if (!goodMix(mix))
       return "woloolooO";                //you are a blue monk now
   
   int cn = mix[0].length(), n = mix.length;   //cn = 3 | n = 6 
   char[] zip=new char[cn*n];
  
   for (int i=0; i<n; i++) 
       for (int x=0; x<cn; x++)
           zip[i+(n*x)] = mix[i].charAt(x);
      
   return  new String(zip);  
}

boolean goodMix(String ... mix)
{
   if (mix.length<2) 
      return false;              
   for (int i=1; i<mix.length; i++)
      if (mix[i].length()!=mix[0].length())
         return false;        
   return true;
}

For example, for the first String: "AAA":

zip[i+(n*x)]=mix[i].charAt(x); // zip[0 + (6*0)]=mix[0].charAt(0); 
zip[i+(n*x)]=mix[i].charAt(x); // zip[0 + (6*1)]=mix[0].charAt(1);
zip[i+(n*x)]=mix[i].charAt(x); // zip[0 + (6*2)]=mix[0].charAt(2); 
          
            zip[0]=A      zip[6]=A     zip[12]=A  

For the last String: "789":

zip[i+(n*x)]=mix[i].charAt(x); // zip[5 + (6*0)]=mix[5].charAt(0); 
zip[i+(n*x)]=mix[i].charAt(x); // zip[5 + (6*1)]=mix[5].charAt(1);
zip[i+(n*x)]=mix[i].charAt(x); // zip[5 + (6*2)]=mix[5].charAt(2); 
          
            zip[5]=7      zip[11]=8     zip[17]=9  

enter image description here

Same output:

 String[] mix =new String[] { "AAA","123","BBB","456","CCC","789"};
 System.out.println(getStringsZippedDirectMove(mix));   //"A1B4C7A2B5C8A3B6C9"

Each iteration leads to the complete relocation of the String element's chars.

2. Multi move from index - Holger style

Motivated by Holger's comments

This also will work regardless of the number of strings to zip, from 2 to n.*

public String getStringsZippedHolger(String ... mix) 
{    
   if (!goodMix(mix))
      return "woloolooO";           //you are a red monk now

   char[] zip = new char[mix[0].length()*mix.length];    
   for (int i=0, j=0; i<mix[0].length(); i++) 
      for (String s : mix)
          zip[j++] = s.charAt(i);

   return new String(zip);  
}

The main loop iterates three times, as it's based on each text's length (3). At each iteration, it will append the char at position i from each of the Strings in the array in the index marked by j. This last counter increments at each assignation.

enter image description here

 String[] mix =new String[] { "AAA","123","BBB","456","CCC","789"};
 System.out.println(getStringsZippedHolger(mix));         // "A1B4C7A2B5C8A3B6C9"

 System.out.println(getStringsZippedHolger("HLE","OGR"));
 System.out.println(getStringsZippedHolger("IT S","SHBS"," EO "));


Original answer block (2 strings)

Arrays

Double assignation at each iteration

public String getZippppppppppppppppppppppppp(String a, String s)  //a -"ABC" s -"123"
{    
   if (s.length()!=a.length())
      return "";
   char[] zip=new char[s.length()*2];      
   for (int i=0; i<s.length(); i++) 
   {
      zip[i*2] = a.charAt(i);   
      zip[(i*2)+1] = s.charAt(i);  
   }  
   return new String(zip);  /* "A1B2C3" */
}

Loop over the length of any of the Strings and insert each element in order. During the iterations, this are the assigned values:

             i = 0              i = 1             i = 2
      --------------------------------------------------------
            zip[0] = A        zip[2] = B         zip[4] = C
            zip[1] = 1        zip[3] = 2         zip[5] = 3
  

Horrendous paint:

enter image description here

As a result, we got:

zip = ['A','1','B','2','C','3'] ||| new String(zip) = "A1B2C3"

Note: If you don't love arrays, you have no hearth.

Single assignation at each iteration

This uses another approach for the iteration logic, which seems totally useless when you can do what the previous example does. but just for fun.

static String getZipppppppppppppppppppppUsingCharAtThanksElliot(String a, String s)  
{                                                                 //a -"ABC" s -"123"
  if (a.length()!=s.length()) 
      return "";
  char[] zip = new char[s.length()*2];
  int c=0;
  boolean even = false;
  for(int i =0; i < (s.length()*2); i++) 
  {
     even =!even;
     if (even) 
        zip[i] = a.charAt(c); 
     else 
     {      
        zip[i] = s.charAt(c);
        c++; 
     }   
   }
   
   return new String(zip);  //--> "A1B2C3"
}

String#subString:

public String getZippppppppppppppppppppppppp(String a, String s)  //a -"ABC" s -"123"
{
    if (a.length()!=s.length()) 
        return "";
    String line="";
    for(int i = 0; i < s.length(); i++)
        line += ( a.substring(i*1, (i*1)+1) + s.substring(i*1, (i*1)+1) );

    return line;  //--> "A1B2C3"
}

Probably the worst performant approach.


String#charAt

Note that charAt(), correctly pointed out on Elliot's answer, will not work with this logic; it will give you a numeric text, as result of adding their respective unicode values. It won't append the characters.

Alternatives to work with charAt() would be using the empty string workaround, or creating a char[] as in the second boring example.

public String getZipppppppppppppppppppppUsingCharAtThanksElliot(String a, String s)  
{                                                                 //a -"ABC" s -"123"
    if (a.length()!=s.length()) 
        return "";
    String line="";
    for(int i = 0; i < s.length(); i++)
        line +=  a.charAt(i) + "" + s.charAt(i) ; //note the empty string

    return line;  //--> "A1B2C3"
}
aran
  • 10,978
  • 5
  • 39
  • 69
  • @Holger I just realized... I didn't mean to define charAt as an alternative approach, but the way the second example iterates through the elements. I should have pointed that, updated it – aran Feb 27 '21 at 02:06
  • 2
    That makes sense. Small slip: `charAt[i]` should be `charAt(i)`. Another alternative would be using two variables: `for(int i = 0, j = 0; i < s.length(); i++) { zip[j++] = a.charAt[i]; zip[j++] = s.charAt[i]; }`. – Holger Feb 27 '21 at 09:44
  • 1
    You can use `String... mix` instead of `String[] mix`, so you can still call it like the original one. – Holger Feb 27 '21 at 11:24
2

You are currently instantiating Zippppp instances and printing as a side-effect of instantiation. That is not what a constructor is intended for, make a static method and return a String. And use a StringBuilder. Something like,

public static String Zippppp(String a, String s) {
    StringBuilder sb = new StringBuilder();
    if (a != null && s != null && a.length() == s.length()) {
        for (int i = 0; i < a.length(); i++) {
            sb.append(a.charAt(i)).append(s.charAt(i));
        }
    }
    return sb.toString();
}

And then to call it

System.out.println(Zippppp("ABC", "123"));
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
2

Do not use charAt, substring on Strings

  1. Using charAt and substring assumes that characters in the String are in Basic Multilingual Plane (BMP)
  2. For any characters in Supplementary Plane(SP), these assumptions will give invalid characters

Use codepoint

  1. Code points should become the default and any processing with char should be deprecated
  2. Use Guava Streams.zip (this is beta for very long years and we can depend)
import com.google.common.collect.Streams;
import java.util.stream.Collectors;

class Zippppp {
    private final String zipped;
    public Zippppp(String a, String b) {
        zipped = Streams.zip(a.codePoints().boxed(), b.codePoints()
            .boxed(), (x, y) -> new String(Character.toChars(x)) + new String(Character.toChars(y)))
            .collect(Collectors.joining());
    }


    public static void main(String[] args){
        System.out.println(new Zippppp("\uD83D\uDE00\uD83D\uDE00\uD83D\uDE00", "123").zipped); // 123
        System.out.println(new Zippppp("ABC", "12").zipped); // A1B2
    }
}
Thiyanesh
  • 2,360
  • 1
  • 4
  • 11
  • 2
    Codepoints alone won’t solve all semantic problems either. You’d need to handle *grapheme clusters*. E.g. “️‍” should not get ripped apart, but consists of four codepoints (six `char`s). – Holger Feb 26 '21 at 15:35
  • @Holger, Thank you for sharing about [`grapheme clusters`](http://www.unicode.org/reports/tr29/). I will read more about them. – Thiyanesh Feb 26 '21 at 15:57