Javascript 填充数组中缺少的数值

Javascript 填充数组中缺少的数值,javascript,ramda.js,Javascript,Ramda.js,假设我有一个数组[0,null,null,3,null,null,null,null,11] 我想用基于上一个和下一个已知数字(和索引?)的数字填充空值,因此得到[0,1,2,3,5,7,9,11]。最有效的方法是什么 我在想一种方法,可以计算两个已知数字之间的空值,然后得到一步的大小。但这些步骤在两人之间是不同的 我正在制作一个图表,其中可能缺少一些值,因此我必须填写可能的值 这是我尝试过的,但我认为这是非常低效和混乱的。我更喜欢使用ramda.js或一些函数方法 const data=[0

假设我有一个数组
[0,null,null,3,null,null,null,null,11]

我想用基于上一个和下一个已知数字(和索引?)的数字填充空值,因此得到
[0,1,2,3,5,7,9,11]
。最有效的方法是什么

我在想一种方法,可以计算两个已知数字之间的空值,然后得到一步的大小。但这些步骤在两人之间是不同的

我正在制作一个图表,其中可能缺少一些值,因此我必须填写可能的值

这是我尝试过的,但我认为这是非常低效和混乱的。我更喜欢使用ramda.js或一些函数方法

const data=[0,null,null,3,null,null,null,11]
const getStep=(arr,lastKnown=0,counter=1)=>{
常数val=arr[0];
如果(val!==null){
返回(val-最近已知)/计数器
}否则{
返回getStep(arr.slice(1),lastKnown,++计数器)
}
}
let lastKnown=null
让currentStep=null
常量filledData=data.map((x,i)=>{
如果(x!==null){
lastKnown=x
currentStep=null
返回x
}
如果(currentStep!==null){
lastKnown=lastKnown+currentStep
}否则{
currentStep=getStep(data.slice(i),最后已知)
}
返回currentStep+lastKnown
})

console.log(filledData)
执行此操作的一种方法是使用for循环和计数:

var skips = 0;
var last;

for (var i=0; i<arr.length; i++){
  var current = arr[i]

  if (current !== null) {

    // If there are skipped spots that need to be filled...
    if (skips > 0){

      // Calculate interval based on on skip count, and difference between current and last
      var interval = (current-arr[last])/(skips+1);

      // Fill in the missing spots in original array
      for (var j=1; j<=skips; j++){
        arr[last+j] = arr[last]+(interval*j)
      }

    }

    last = i; // update last valid index
    skips = 0; // reset skip count
  }
  // If null, just increment skip count
  else { 
    skips++
  }
}
var跳过=0;
var last;
对于(变量i=0;i 0){
//基于跳过计数以及当前和上次之间的差值计算间隔
var间隔=(当前arr[上次]/(跳过+1);
//填充原始数组中缺少的点

对于(var j=1;j,您可以迭代数组,如果找到
null
值,则会提前查看下一个数字和间隙,直到采用线性方法填充该数字

var数组=[0,null,null,3,null,null,null,11],
i=0,j,δ;
while(iconsole.log(数组);
以下是我使用ramda的快速解决方案:

const xs = [0, null, null, 3, null, null, null, 11]
const scanWithIndex = R.addIndex(R.scan)
const notNil = R.complement(R.isNil)
const mapWithIndex = R.addIndex(R.map)
const zipArrays = R.zipWith(R.concat)

// number of cons nulls for nth element
const consNulls = R.drop(1, R.scan((acc, x) => R.isNil(x) ? (acc + 1) : 0, 0, xs))

// length of ongoing null sequence for each element
const consNullsSeqLens = R.drop(1, scanWithIndex((acc, x, ind) =>{
  if (x !== 0 && acc !== 0) return acc
  const rest = R.drop(ind, consNulls)
  return R.findIndex(R.equals(0), rest)
}, 0, consNulls))

// previous non null value for each el
const prevNonNulls = R.scan((acc, x) => R.isNil(x) ? acc : x, 0, xs)

// next non null value for each el
const nextNonNulls = mapWithIndex((x, ind) => {
  const rest = R.drop(ind, xs)
  return R.find(notNil, rest)
}, xs)

// function to calculate missing values based on zipped arrays
const calculateMissingValue = ([x, seqN, seqLen, next, prev]) =>
  R.isNil(x) ? prev + (next - prev) / (seqLen + 1) * seqN : x 

R.map(
  calculateMissingValue,
  // zips 5 lists together
  zipArrays(
    zipWith(R.append, consNullsSeqLens, R.zip(xs, consNulls)),
    R.zip(nextNonNulls,prevNonNulls)
 )
)

虽然从节目中可以看出,您可以使用Ramda来实现这一点,但我不确定Ramda是否会有很大帮助。(免责声明:我是Ramda的作者之一。)

我第一次看这个是这样的:

const intersperseNulls = pipe(
  reduce(
    ({vals, prev, nilCount}, curr) => isNil(curr) 
       ? {vals: vals, prev: prev, nilCount: nilCount + 1} 
       : (nilCount < 1) 
         ? {vals: append(curr, vals), prev: curr, nilCount: 0}
         : {
             vals: append(curr, concat(vals, times(n => prev + (n + 1) * (curr - prev) / (nilCount + 1), nilCount))), 
              prev: curr, 
              nilCount: 0
           },
    {vals: [], prev: undefined, nilCount: 0},
  ),
  prop('vals')
);
只有
难以更换

但最后,我更喜欢from。它更简单,更容易阅读,而且不需要任何诡计


您可以在

中看到这些。另一种方法是将输入数组转换为一个“段”列表,捕获每个段的起始值、结束值和大小。然后,您可以使用在每个段的起始值和结束值之间的线性步长来建立列表

const input=[0,null,null,3,null,null,null,11]
//递归地将稀疏的数字列表转换为段列表
const segmentNull=xs=>{
如果(xs.length==0){
返回[]
}否则{
常数[y,…ys]=xs
常数计数=R.takeWhile(R.isNil,ys)。长度+1
const next=R.dropWhile(R.isNil,ys)
返回next.length>0
?R.prepend({start:y,end:next[0],count},segmentNull(next))
: []
}
}
//段空(输入)
//=>[{“计数”:3,“结束”:3,“开始”:0},{“计数”:4,“结束”:11,“开始”:3}]
//在“开始”和“结束”值之间线性生成“计数”值列表
常数线性排列=(开始、结束、计数)=>
R.次(n=>(结束-开始)*(n+1)/计数+开始,计数)
//线性排列(3、11、4)
//=> [5, 7, 9, 11]
//将线段列表转换为线段之间的线性值列表
const buildListFromSegments=R.chain({start,end,count})=>
线性排列(开始、结束、计数))
//buildListFromSegments(segmentNull(输入))
//=> [1, 2, 3, 5, 7, 9, 11]
//^--请注意,缺少前导0
//在“buildListFromSegments”的结果前面加上初始值`
const fn=xs=>R.prepend(xs[0],buildListFromSegments(segmentNull(xs)))
console.log(fn(输入))
一种O(n*m)解,其中n是所有元素的计数,m是空值的计数

算法假设在索引位置0和长度1处始终存在有效的数字

函数填充空格(a){
var s,//步骤
r=a.reduce(函数([t,ns,r],e){/[temp,nulls数组,结果累加器]
e===null?ns.push(e)
:t==void 0?t=e
:(s=(e-t)/(ns.长度+1),
r、 推(t,…ns.map((u,i)=>t+(i+1)*s)),
ns=[],
t=e);
返回[t,ns,r];
},[void 0,[],[]]);
返回r[2]。concat(r[0]);
}
var arr=[0,null,null,3,null,null,null,11],
res=填充空白(arr);

console.log(JSON.stringify(res));
您可以添加代码(an)你已经尝试过了。我已经添加了我的尝试,但是我有点为这个代码感到羞耻:如果列表以
null
开头或结尾,那么预期的行为是什么?你能让这个答案符合TS/ES6吗?这看起来很混乱。嗯,这似乎在我们的测试中失败了。谢谢。有些值没有正确推断请添加你的test数组。它太大,无法在此处添加,但我将提供1secacutally-no链接。显然,我们正在尝试匹配的另一个C#lib存在此问题。因此,您的代码工作得非常好。很抱歉发出此警报。我的错。
const steps = (b, e, c) => {
  const results = []
  for (let i = 0; i < c; i++) {results.push(b + (i + 1) * (e - b) / (c + 1));}
  return results;
}

const intersperseNulls = array => array.reduce(
  ({vals, prev, nilCount}, curr) => (curr == null) 
    ? {vals: vals, prev: prev, nilCount: nilCount + 1} 
    : (nilCount < 1) 
      ? {vals: vals.concat(curr), prev: curr, nilCount: 0}
      : {
          vals: vals.concat(steps(prev, curr, nilCount)).concat(curr), 
          prev: curr, 
          nilCount: 0
        },
  {vals: [], prev: undefined, nilCount: 0},
).vals