0

I am asking for help to solve the problem renaming the strings in array that looks like this:

["a(1)","a(6)","a","a","a","a","a","a","a","a","a","a"]

After the function execution it should look as follows:

["a(1)","a(6)","a","a(2)","a(3)","a(4)","a(5)","a(7)","a(8)","a(9)","a(10)","a(11)"]

Empty array and array free of duplicates should left untouched.

My idea is to populate an empty object with key/value pairs and then just push them to a new array:

function renameFiles(arr){
    var itemsObj = {};
    var count = 1;
    for (var i = 0; i < arr.length; i++){

        itemsObj[arr[i]] = count;
        // if the key present, rename the array item and add it to the 
        // itemsObj
        if (arr[i] in itemsObj){
            itemsObj[arr[i] + '(' + (i - (i - 1)) + ')']
        }

    }
    console.log(itemsObj)
    // once the itmesObj is set, run the loop and push the keys to the 
    // array
    return arr;

}

var array = ["a(1)","a(6)","a","a","a","a","a","a","a","a","a","a"]
renameFiles(array);

The problem is that the itemsObj is not get populated with duplicates keys. There should be some other method that can handle this task. I am a beginner and probably not aware of that method.

4 Answers 4

3

You're almost there. You'd keep a count, and check for duplicates, and then do another check for duplicates with parentheses, and update the count appropriately

function renameFiles(arr){
  var count = {};
  arr.forEach(function(x,i) {

    if ( arr.indexOf(x) !== i ) {
      var c = x in count ? count[x] = count[x] + 1 : count[x] = 1;
      var j = c + 1;
      var k = x + '(' + j + ')';

      while( arr.indexOf(k) !== -1 ) k = x + '(' + (++j) + ')';
      arr[i] = k;
    }
  });
  return arr;
}

var res = renameFiles(["a(1)","a(6)","a","a","a","a","a","a","a","a","a","a"]);
console.log(res)
.as-console-wrapper {top:0; max-height:100%!important}

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

5 Comments

Hmmm. you learn something new every day. didn't know you could increment inside a ternary operator.
@Skam - You can increment just fine, but relying on the result of assigment, as I'm doing above, should be avoided in many cases, at least in my opinion. I did it here to avoid a whole if/else mess checking for keys etc, and I think it's fine as there shouldn't be any issues doing it this way in this specific case !
@adeneo Would you please explain this line var c = x in count ? count[x] = count[x] + 1 : count[x] = 1; I am not familiar yet with this kind of style.
Sure, if x is in count, then do count[x] = count[x] + 1 which just adds one to the count, else if x is not in count, set it to 1. As assignment always return the assigned value, I'm getting whatever is set back in c, i.e. the current count.
@adeno Thank you sir!! Your are very helpful.
1

You were on the right track.

Another solution,

(function(){
    var renameFiles = function(arr){
        var counts = {}
        for(var i=0;i<arr.length;i++){
            if(!counts[arr[i]])
                counts[arr[i]]=0;
            counts[arr[i]]++;
        }
        arr = [];
        for(var name in counts){
            for(var i=0;i<counts[name];i++){
                arr.push(name+(i===0?'':'('+i+')'));
            }
        }
        return arr;
    }

    var array = ["a(1)","a(6)","a","a","a","a","a","a","a","a","a","a"];
    console.log(renameFiles(array))
})();

Comments

0
var arr = ["a(1)","a(6)","a","a","a","a","a","a","a","a","a","a"]

function renameFiles(arr){
  var dup_count, new_name;
  arr.forEach((item, index) => {
    dup_count = arr.filter(x => x == item).length;
    if (dup_count > 1) {
      for(n = 0; n < dup_count;){
        do {
          new_name = `${item}(${n+=1})`;
        } while (arr.includes(new_name));
        arr[arr.indexOf(item)] = new_name;
      }
    }
  });
  return arr
}
> renameFiles(arr)
< (12) ["a(1)", "a(6)", "a(2)", "a(3)", "a(4)", "a(5)", "a(7)", "a(8)", "a(9)", "a(10)", "a(11)", "a"]

Comments

0

To achieve the same result without mutation you can use the following code. Whilst it is much more code a majority of the functions I have included can be replaced with functions included with Ramda or another FP library. I find the following more readable, however that is simple a matter of preference.

Working sandbox here.

const incrementingList = (n) => [...Array(n).keys()];
const formatStr = (key) => ifElse(equals(0))(always(key))(concat(key));

const incValues = (obj) =>
  flatMap((key) => map(formatStr(key))(incrementingList(obj[key])))(keys(obj));

const incOrInit = (record, key) =>
  isNil(record[key])
    ? assoc(key)(1)(record)
    : assoc(key)(inc(record[key]))(record);

const generateCounts = reduce({})(incOrInit);
const renameList = compose(incValues, generateCounts);

const list = ["a", "b", "b", "b", "a", "a", "c", "c", "c", "d"];
const result = renameList(list);
console.log(result); // => ["a", "a1", "a2", "b", "b1", "b2", "c", "c1", "c2", "d"]

// THESE FUNCTIONS CAN BE REPLACE WITH RAMDA \\

function keys(obj) {
  return Object.keys(obj);
}

function ifElse(cond) {
  return function ifTrueFn(trueFn) {
    return function ifFalseFn(falseFn) {
      return function passValue(value) {
        return cond(value) ? trueFn(value) : falseFn(value);
      };
    };
  };
}

function always(value) {
  return function alwaysInner() {
    return value;
  };
}

function concat(a) {
  return function inner(b) {
    return a.concat(b);
  };
}

function equals(a) {
  return function equalsInner(b) {
    return a === b;
  };
}

function compose2(fn1, fn2) {
  return function passArg(...args) {
    return fn1(fn2(...args));
  };
}

function compose(...fns) {
  return fns.reduce(compose2);
}

function flatMap(mapFn) {
  return function inner(list) {
    return list.flatMap(mapFn);
  };
}

function map(mapFn) {
  return function passList(list) {
    return list.map(mapFn);
  };
}

function reduce(init) {
  return function reducer(reducer) {
    return function data(data) {
      return data.reduce(reducer, init);
    };
  };
}

function assoc(key) {
  return function assocValue(value) {
    return function assocObject(obj) {
      return { ...obj, [key]: value };
    };
  };
}

function inc(n) {
  return n + 1;
}

function isNil(value) {
  return value == null;
}

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.