0
\$\begingroup\$

I wrote an update() function to make an entry the first element in an array.

var arr = [
    {v: 1},
    {v: 2},
    {v: 3},
    {v: 4},
];

function update(e) {
    var i = arr.findIndex(o => o.v === e.v);
    if (i === 0) return;
    if (i > 0) arr = [e].concat(arr.slice(0, i), arr.slice(i+1));
    if (i < 0) arr = [e].concat(arr.slice(0, arr.length - 1)); // keep the array has 4 elements
}

update({v: 1});
update({v: 2});
update({v: 6});

Is there any better way to do this? I think slice and concat will make the performance not good.

\$\endgroup\$
1
\$\begingroup\$
function update(e) {

    // find index
    var i = arr.findIndex(o => o.v === e.v);

    // if already at start, nothing to do
    if (i === 0) return;

    // remove old occurrency, if existing
    if (i > 0) {
        arr.splice( i, 1 );
    }

    // add e to the start
    arr.unshift( e );

    // keep array at the correct size
    arr.length = Math.min( arr.length, 4 );
}

Somewhat less code. I don't know about the performance.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ but this is exactly what I need, I want to keep the array has 5 elements \$\endgroup\$ – roger Aug 26 '16 at 13:41
  • \$\begingroup\$ @roger You did not mention that in your question. I corrected my code, though. \$\endgroup\$ – Sirko Aug 26 '16 at 13:43
0
\$\begingroup\$

Wow lots of inefficient operations... Why don't you just swap the first one with the one you want to be the first ... even by de-structuring just to make it look cooler.

This is how i would do it;

var arr = [
           {v: 1},
           {v: 2},
           {v: 3},
           {v: 4},
          ],
 update = (a,e) => { var i = a.findIndex(o => o.v === e.v);
                     i > 0 ? ([a[0],a[i]] = [a[i],a[0]]) : i && a.splice(0,0,e);
                     return a;
                   };

update(arr,{v: 1});
console.log(arr);
update(arr,{v: 2});
console.log(arr);
update(arr,{v: 6});
console.log(arr);

Well OK. as per @roger's comment if swap is not desired the following snippet should handle the requested action. It removes the requested element and inserts it at index 0 position.

var arr = [
    {v: 1},
    {v: 2},
    {v: 3},
    {v: 4},
];

 update = (a,e) => { var i = a.findIndex(o => o.v === e.v);
                     i > 0 ? a.splice(0,0,a.splice(i,1)[0]) 
                           : i && a.splice(0,0,e);
                     (a.length > 5) && a.length--
                     return a;
                   };

update(arr,{v: 3});
console.log(arr);

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ two problems: 1. try update(arr, {v: 3}), you will find that your result is different from mine; 2. this syntax [a[0],a[i]] = [a[i],a[0]] actually not support in old node.js, such as v4.0.0 \$\endgroup\$ – roger Aug 28 '16 at 3:13
  • \$\begingroup\$ @roger the second snippet should do it. I remember i had tested splice operations performance with slice and concat combo (stackoverflow.com/a/37220725/4543207) and it was superior. \$\endgroup\$ – Redu Aug 28 '16 at 6:13
  • \$\begingroup\$ maybe you should edit your answer again, try update(arr, {v: 4}). Although it is a simple problem. \$\endgroup\$ – roger Aug 28 '16 at 7:35
  • \$\begingroup\$ @roger.. Wow what was that 2.? Well it was a leftover from my console tests. I should be more careful when copy pasting. Thanks. \$\endgroup\$ – Redu Aug 28 '16 at 7:39
0
\$\begingroup\$

Here's a functional solution that might perform well on a browser with tail recursive optimizations. Unfortunately, jsperf is down, so I can't get any metrics for you.

var arr = [
    {v: 1},
    {v: 2},
    {v: 3},
    {v: 4},
];

const moveFirstToHead = ([head, ...tail], predicate, accumulator = []) =>
  (predicate.call(null, head)) ?
    [head].concat(accumulator).concat(tail) :
    moveFirstToHead(tail, predicate, accumulator.concat(head));


const result = moveFirstToHead(arr, element => element.v === 3);
console.log(result);

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

There are several modes when checking equality, Abstact (==), Strict (===), Same Value Zero, Same Value (Object.is) and some others like nodes deepEqual. Each will give you different results, depending on your values. I will go with Strict equality as that is what you have used.

You have tagged the question ES6, so I will go with that and I am going to use the iterator feature.

I notice that you wish to keep the array the same length as the original, even if the supplied item is prepended to the array. You also speak like scaling is going to be an issue, so I will add some tests that will use a reasonably large data set.

var arr = [
    {v: 1},
    {v: 2},
    {v: 3},
    {v: 4},
];

// using iterators
function update(e) {
  const length = arr.length;
  const it = arr.entries();
  let item = it.next();
  while (!item.done) {
    if (item.value[1].v === e.v) {
      if (item.value[0] === 0) {
        return;
      }
      e = arr.splice(item.value[0], 1)[0];
      break;
    }
    item = it.next();
  }
  arr.unshift(e);
  arr.length = length;
}

update({v: 1});
update({v: 2});
update({v: 6});
console.log(arr);

And here are some tests.

const Darr = [];
for (let x = 0; x < 100000; x += 1) {
	Darr.push({v: x});
}
console.log('Original array length: ', Darr.length);
let arr;

function test(fn) {
	arr = Darr.slice();
  let count = 1000;
  const Ts = performance.now();
  while (count) {
    fn({v: 1});
    fn({v: 2});
    fn({v: 10000001});
    count -= 1;
  }
  const Te = performance.now();
  console.log(`name: ${fn.name} time: ${Math.ceil(Te - Ts)} items: ${arr.length}`);
}

// code from your question
function update(e) {
    var i = arr.findIndex(o => o.v === e.v);
    if (i === 0) return;
    if (i > 0) arr = [e].concat(arr.slice(0, i), arr.slice(i+1));
    if (i < 0) arr = [e].concat(arr.slice(0, arr.length - 1)); // keep the array has 4 elements
}
test(update);

// using iterators
function update1(e) {
  const length = arr.length;
  const it = arr.entries();
  let item = it.next();
  while (!item.done) {
    if (item.value[1].v === e.v) {
      if (item.value[0] === 0) {
        return;
      }
      e = arr.splice(item.value[0], 1)[0];
      break;
    }
    item = it.next();
  }
  arr.unshift(e);
  arr.length = length;
}
test(update1);

// other answers
function update2(e) {

    // find index
    var i = arr.findIndex(o => o.v === e.v);

    // if already at start, nothing to do
    if (i === 0) return;

    // remove old occurrency, if existing
    if (i > 0) {
        arr.splice( i, 1 );
    }

    // add e to the start
    arr.unshift( e );

    // keep array at the correct size
    arr.length = Math.min( arr.length, 4 );
}
test(update2);

const update3 = (e) => { var i = arr.findIndex(o => o.v === e.v);
                     i > 0 ? arr.splice(0,0,arr.splice(i,1)[0]) 
                           : i && arr.splice(0,0,e);
                     (arr.length > 5) && arr.length--
                     return arr;
                   };
test(update3);

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.